From 2aaf4bf96b960d6379b4c089145031d18c065aa3 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 15 Oct 2023 19:14:34 +0200 Subject: [PATCH 01/31] fix typo in build.rs that caused fmc ClkPin to not be implemented --- 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 8e680fb6..710e1185 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -797,7 +797,7 @@ fn main() { (("fmc", "NCE"), quote!(crate::fmc::NCEPin)), (("fmc", "NOE"), quote!(crate::fmc::NOEPin)), (("fmc", "NWE"), quote!(crate::fmc::NWEPin)), - (("fmc", "Clk"), quote!(crate::fmc::ClkPin)), + (("fmc", "CLK"), quote!(crate::fmc::ClkPin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), From b1e5b6ffe1587362d4a58f5da831338dfa41a1bc Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 28 Oct 2023 13:50:02 +0200 Subject: [PATCH 02/31] Add raw fmc access implementation --- embassy-stm32/src/fmc.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index d6e25996..dd0d2721 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -12,6 +12,37 @@ pub struct Fmc<'d, T: Instance> { unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {} +impl<'d, T> Fmc<'d, T> +where + T: Instance, +{ + /// Create a raw FMC instance. + /// + /// **Note:** This is currently used to provide access to some basic FMC functions + /// for manual configuration for memory types that stm32-fmc does not support. + pub fn new_raw(_instance: impl Peripheral

+ 'd) -> Self { + Self { peri: PhantomData } + } + + /// Enable the FMC peripheral and reset it. + pub fn enable(&mut self) { + T::enable_and_reset(); + } + + /// Enable the memory controller on applicable chips. + pub fn memory_controller_enable(&mut self) { + // 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_v1x3, fsmc_v2x3, fsmc_v3x1)))] + T::REGS.bcr1().modify(|r| r.set_fmcen(true)); + } + + pub fn source_clock_hz(&self) -> u32 { + ::frequency().0 + } +} + unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T> where T: Instance, From fa45dcd0348379e7b9714f9b845fa3eb75671890 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 5 Nov 2023 15:41:31 +0000 Subject: [PATCH 03/31] Add raw USB example using control transfers --- examples/stm32f4/src/bin/usb_raw.rs | 201 ++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 examples/stm32f4/src/bin/usb_raw.rs diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs new file mode 100644 index 00000000..8d4e6c7d --- /dev/null +++ b/examples/stm32f4/src/bin/usb_raw.rs @@ -0,0 +1,201 @@ +//! Example of using USB without a pre-defined class, but instead responding to +//! raw USB control requests. +//! +//! The host computer can either: +//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer +//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive +//! +//! For higher throughput data, you can add some bulk endpoints after creating the alternate, +//! but for low rate command/response, plain control transfers can be very simple and effective. +//! +//! Example code to send/receive data using `nusb`: +//! +//! ```ignore +//! use futures_lite::future::block_on; +//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient}; +//! +//! fn main() { +//! let di = nusb::list_devices() +//! .unwrap() +//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe) +//! .expect("no device found"); +//! let device = di.open().expect("error opening device"); +//! let interface = device.claim_interface(0).expect("error claiming interface"); +//! +//! // Send "hello world" to device +//! let result = block_on(interface.control_out(ControlOut { +//! control_type: ControlType::Vendor, +//! recipient: Recipient::Interface, +//! request: 100, +//! value: 200, +//! index: 0, +//! data: b"hello world", +//! })); +//! println!("{result:?}"); +//! +//! // Receive "hello" from device +//! let result = block_on(interface.control_in(ControlIn { +//! control_type: ControlType::Vendor, +//! recipient: Recipient::Interface, +//! request: 101, +//! value: 201, +//! index: 0, +//! length: 5, +//! })); +//! println!("{result:?}"); +//! } +//! ``` + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::usb_otg::Driver; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; +use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType}; +use embassy_usb::types::InterfaceNumber; +use embassy_usb::{Builder, Handler}; +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!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL168, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. + divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let mut ep_out_buffer = [0u8; 256]; + 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); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-raw example"); + config.serial_number = Some("12345678"); + + // 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; + 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 handler = ControlHandler { + if_num: InterfaceNumber(0), + }; + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Add a vendor-specific function (class 0xFF), and corresponding interface, + // that uses our custom handler. + let mut function = builder.function(0xFF, 0, 0); + let mut interface = function.interface(); + let _alternate = interface.alt_setting(0xFF, 0, 0, None); + handler.if_num = interface.interface_number(); + drop(function); + builder.handler(&mut handler); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + usb.run().await; +} + +/// Handle CONTROL endpoint requests and responses. For many simple requests and responses +/// you can get away with only using the control endpoint. +struct ControlHandler { + if_num: InterfaceNumber, +} + +impl Handler for ControlHandler { + /// Respond to HostToDevice control messages, where the host sends us a command and + /// optionally some data, and we can only acknowledge or reject it. + fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option { + // Log the request before filtering to help with debugging. + info!("Got control_out, request={}, buf={:a}", req, buf); + + // Only handle Vendor request types to an Interface. + if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface { + return None; + } + + // Ignore requests to other interfaces. + if req.index != self.if_num.0 as u16 { + return None; + } + + // Accept request 100, value 200, reject others. + if req.request == 100 && req.value == 200 { + Some(OutResponse::Accepted) + } else { + Some(OutResponse::Rejected) + } + } + + /// Respond to DeviceToHost control messages, where the host requests some data from us. + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + info!("Got control_in, request={}", req); + + // Only handle Vendor request types to an Interface. + if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface { + return None; + } + + // Ignore requests to other interfaces. + if req.index != self.if_num.0 as u16 { + return None; + } + + // Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject. + if req.request == 101 && req.value == 201 && req.length == 5 { + buf[..5].copy_from_slice(b"hello"); + Some(InResponse::Accepted(&buf[..5])) + } else { + Some(InResponse::Rejected) + } + } +} From 0272deb158c4cc821e8f587283817a0fe5f82cf8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Nov 2023 23:35:01 +0100 Subject: [PATCH 04/31] stm32/rcc: add shared code for hsi48 with crs support. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/g4.rs | 50 ++------------- embassy-stm32/src/rcc/h.rs | 16 +---- embassy-stm32/src/rcc/hsi48.rs | 62 +++++++++++++++++++ embassy-stm32/src/rcc/l0l1.rs | 38 ++---------- embassy-stm32/src/rcc/l4l5.rs | 26 ++++---- embassy-stm32/src/rcc/mod.rs | 5 ++ embassy-stm32/src/rcc/u5.rs | 9 +-- examples/stm32g4/src/bin/usb_serial.rs | 6 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/usb_serial.rs | 46 +++++++------- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- .../stm32h7/src/bin/low_level_timer_api.rs | 1 - examples/stm32h7/src/bin/rng.rs | 2 +- examples/stm32h7/src/bin/usb_serial.rs | 2 +- examples/stm32l0/src/bin/button_exti.rs | 3 +- examples/stm32l0/src/bin/lora_cad.rs | 2 +- examples/stm32l0/src/bin/lora_lorawan.rs | 2 +- examples/stm32l0/src/bin/lora_p2p_receive.rs | 2 +- examples/stm32l0/src/bin/lora_p2p_send.rs | 2 +- .../src/bin/spe_adin1110_http_server.rs | 2 +- examples/stm32l4/src/bin/usb_serial.rs | 2 +- examples/stm32u5/src/bin/usb_serial.rs | 3 +- tests/stm32/src/common.rs | 6 +- 25 files changed, 136 insertions(+), 161 deletions(-) create mode 100644 embassy-stm32/src/rcc/hsi48.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2a2df5c5..7e11e263 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ 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 } critical-section = "1.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8381654ade324de3945c3c755d359686e957e99b" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8381654ade324de3945c3c755d359686e957e99b", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index b14a6197..13eb0c48 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -7,7 +7,6 @@ pub use crate::pac::rcc::vals::{ Pllr as PllR, Ppre as APBPrescaler, }; use crate::pac::{PWR, RCC}; -use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -67,23 +66,13 @@ pub struct Pll { 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), + Hsi48(super::Hsi48Config), /// 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, @@ -102,12 +91,6 @@ pub struct Config { pub ls: super::LsConfig, } -/// 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 { #[inline] fn default() -> Config { @@ -118,7 +101,7 @@ impl Default for Config { apb2_pre: APBPrescaler::DIV1, low_power_run: false, pll: None, - clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(None)), + clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), adc12_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE, ls: Default::default(), @@ -288,33 +271,8 @@ pub(crate) unsafe fn init(config: Config) { crate::pac::rcc::vals::Clk48sel::PLL1_Q } - 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_and_reset(); - - 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); - }); - } + Clock48MhzSrc::Hsi48(config) => { + super::init_hsi48(config); crate::pac::rcc::vals::Clk48sel::HSI48 } }; diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 7e924f0a..4407d9e9 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -21,9 +21,6 @@ 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); - const VCO_RANGE: RangeInclusive = Hertz(150_000_000)..=Hertz(420_000_000); #[cfg(any(stm32h5, pwr_h7rm0455))] const VCO_WIDE_RANGE: RangeInclusive = Hertz(128_000_000)..=Hertz(560_000_000); @@ -126,7 +123,7 @@ pub struct Config { pub hsi: Option, pub hse: Option, pub csi: bool, - pub hsi48: bool, + pub hsi48: Option, pub sys: Sysclk, pub pll1: Option, @@ -155,7 +152,7 @@ impl Default for Config { hsi: Some(HSIPrescaler::DIV1), hse: None, csi: false, - hsi48: false, + hsi48: Some(Default::default()), sys: Sysclk::HSI, pll1: None, pll2: None, @@ -301,14 +298,7 @@ pub(crate) unsafe fn init(config: Config) { }; // 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) - } - }; + let _hsi48 = config.hsi48.map(super::init_hsi48); // Configure CSI. RCC.cr().modify(|w| w.set_csion(config.csi)); diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs new file mode 100644 index 00000000..19a8c8cb --- /dev/null +++ b/embassy-stm32/src/rcc/hsi48.rs @@ -0,0 +1,62 @@ +#![allow(unused)] + +use crate::pac::crs::vals::Syncsrc; +use crate::pac::{CRS, RCC}; +use crate::rcc::sealed::RccPeripheral; +use crate::time::Hertz; + +/// HSI48 speed +pub const HSI48_FREQ: Hertz = Hertz(48_000_000); + +/// Configuration for the HSI48 clock +#[derive(Clone, Copy, Debug)] +pub struct Hsi48Config { + /// Enable CRS Sync from USB Start Of Frame (SOF) events. + /// Required if HSI48 is going to be used as USB clock. + /// + /// Other use cases of CRS are not supported yet. + pub sync_from_usb: bool, +} + +impl Default for Hsi48Config { + fn default() -> Self { + Self { sync_from_usb: false } + } +} + +pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz { + // Enable VREFINT reference for HSI48 oscillator + #[cfg(stm32l0)] + crate::pac::SYSCFG.cfgr3().modify(|w| { + w.set_enref_hsi48(true); + w.set_en_vrefint(true); + }); + + // Enable HSI48 + #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba, stm32f0)))] + let r = RCC.crrcr(); + #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba))] + let r = RCC.cr(); + #[cfg(any(stm32f0))] + let r = RCC.cr2(); + + r.modify(|w| w.set_hsi48on(true)); + while r.read().hsi48rdy() == false {} + + if config.sync_from_usb { + crate::peripherals::CRS::enable_and_reset(); + + CRS.cfgr().modify(|w| { + w.set_syncsrc(Syncsrc::USB); + }); + + // 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); + }); + } + + HSI48_FREQ +} diff --git a/embassy-stm32/src/rcc/l0l1.rs b/embassy-stm32/src/rcc/l0l1.rs index 3af27959..25a7762a 100644 --- a/embassy-stm32/src/rcc/l0l1.rs +++ b/embassy-stm32/src/rcc/l0l1.rs @@ -3,8 +3,6 @@ pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Plldiv as PllDiv, Pllmul as PLLMul, Pllmul as PllMul, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc, }; -#[cfg(crs)] -use crate::pac::{crs, CRS, SYSCFG}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -47,7 +45,7 @@ pub struct Config { pub hsi: bool, pub hse: Option, #[cfg(crs)] - pub hsi48: bool, + pub hsi48: Option, pub pll: Option, @@ -68,7 +66,7 @@ impl Default for Config { hse: None, hsi: false, #[cfg(crs)] - hsi48: false, + hsi48: Some(Default::default()), pll: None, @@ -174,37 +172,11 @@ pub(crate) unsafe fn init(config: Config) { let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); #[cfg(crs)] - if config.hsi48 { - // Reset CRS peripheral - RCC.apb1rstr().modify(|w| w.set_crsrst(true)); - RCC.apb1rstr().modify(|w| w.set_crsrst(false)); - - // Enable CRS peripheral - RCC.apb1enr().modify(|w| w.set_crsen(true)); - - // Initialize CRS - CRS.cfgr().write(|w| - - // Select LSE as synchronization source - w.set_syncsrc(crs::vals::Syncsrc::LSE)); - CRS.cr().modify(|w| { - w.set_autotrimen(true); - w.set_cen(true); - }); - - // Enable VREFINT reference for HSI48 oscillator - SYSCFG.cfgr3().modify(|w| { - w.set_enref_hsi48(true); - w.set_en_vrefint(true); - }); - + let _hsi48 = config.hsi48.map(|config| { // Select HSI48 as USB clock RCC.ccipr().modify(|w| w.set_hsi48msel(true)); - - // Enable dedicated USB clock - RCC.crrcr().modify(|w| w.set_hsi48on(true)); - while !RCC.crrcr().read().hsi48rdy() {} - } + super::init_hsi48(config) + }); set_freqs(Clocks { sys: sys_clk, diff --git a/embassy-stm32/src/rcc/l4l5.rs b/embassy-stm32/src/rcc/l4l5.rs index 44748620..c4483910 100644 --- a/embassy-stm32/src/rcc/l4l5.rs +++ b/embassy-stm32/src/rcc/l4l5.rs @@ -58,8 +58,8 @@ pub struct Config { pub msi: Option, pub hsi: bool, pub hse: Option, - #[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] - pub hsi48: bool, + #[cfg(crs)] + pub hsi48: Option, // pll pub pll: Option, @@ -108,8 +108,8 @@ impl Default for Config { pllsai1: None, #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] pllsai2: None, - #[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] - hsi48: true, + #[cfg(crs)] + hsi48: Some(Default::default()), #[cfg(any(stm32l4, stm32l5, stm32wb))] clk48_src: Clk48Src::HSI48, ls: Default::default(), @@ -126,7 +126,8 @@ pub const WPAN_DEFAULT: Config = Config { prescaler: HsePrescaler::DIV1, }), mux: ClockSrc::PLL1_R, - hsi48: true, + #[cfg(crs)] + hsi48: Some(super::Hsi48Config { sync_from_usb: false }), msi: None, hsi: false, clk48_src: Clk48Src::PLL1_Q, @@ -216,15 +217,10 @@ pub(crate) unsafe fn init(config: Config) { hse.freq }); - #[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] - let hsi48 = config.hsi48.then(|| { - RCC.crrcr().modify(|w| w.set_hsi48on(true)); - while !RCC.crrcr().read().hsi48rdy() {} - - Hertz(48_000_000) - }); - #[cfg(any(stm32l47x, stm32l48x))] - let hsi48 = None; + #[cfg(crs)] + let _hsi48 = config.hsi48.map(super::init_hsi48); + #[cfg(not(crs))] + let _hsi48: Option = None; let _plls = [ &config.pll, @@ -275,7 +271,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src)); #[cfg(any(stm32l4, stm32l5, stm32wb))] let _clk48 = match config.clk48_src { - Clk48Src::HSI48 => hsi48, + Clk48Src::HSI48 => _hsi48, Clk48Src::MSI => msi, Clk48Src::PLLSAI1_Q => pllsai1.q, Clk48Src::PLL1_Q => pll.q, diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c11a9cc6..e15f4fe4 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -9,6 +9,11 @@ mod mco; pub use bd::*; pub use mco::*; +#[cfg(crs)] +mod hsi48; +#[cfg(crs)] +pub use hsi48::*; + #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 2bbacbbd..c111362b 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -115,7 +115,7 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub apb3_pre: APBPrescaler, - pub hsi48: bool, + pub hsi48: Option, /// The voltage range influences the maximum clock frequencies for different parts of the /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks /// exceeding 55 MHz require at least `RANGE2`. @@ -189,7 +189,7 @@ impl Default for Config { apb1_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1, apb3_pre: APBPrescaler::DIV1, - hsi48: true, + hsi48: Some(Default::default()), voltage_range: VoltageScale::RANGE3, ls: Default::default(), } @@ -322,10 +322,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - if config.hsi48 { - RCC.cr().modify(|w| w.set_hsi48on(true)); - while !RCC.cr().read().hsi48rdy() {} - } + let _hsi48 = config.hsi48.map(super::init_hsi48); // The clock source is ready // Calculate and set the flash wait states diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 9099b609..188988b1 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -4,7 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc}; +use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSrc}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{self, Driver, Instance}; use embassy_stm32::{bind_interrupts, peripherals, Config}; @@ -41,9 +41,7 @@ async fn main(_spawner: Spawner) { 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, - }))); + config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); } else { config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); } diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 5bec9d44..b2758cba 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -37,7 +37,7 @@ async fn net_task(stack: &'static Stack) -> ! { async fn main(spawner: Spawner) -> ! { let mut config = Config::default(); config.rcc.hsi = None; - config.rcc.hsi48 = true; // needed for rng + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.hse = Some(Hse { freq: Hertz(8_000_000), mode: HseMode::BypassDigital, diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 735826a6..13b218d0 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -4,9 +4,6 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ - AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, -}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config}; @@ -23,26 +20,29 @@ bind_interrupts!(struct Irqs { #[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: PllPreDiv::DIV2, - mul: PllMul::MUL125, - divp: Some(PllDiv::DIV2), // 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::PLL1_P; - config.rcc.voltage_scale = VoltageScale::Scale0; + { + use embassy_stm32::rcc::*; + config.rcc.hsi = None; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: 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: PllPreDiv::DIV2, + mul: PllMul::MUL125, + divp: Some(PllDiv::DIV2), // 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::PLL1_P; + config.rcc.voltage_scale = VoltageScale::Scale0; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index e37d8797..b7a07737 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -36,7 +36,7 @@ async fn main(spawner: Spawner) -> ! { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for RNG + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 88df53f0..f0f28ec9 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -37,7 +37,7 @@ async fn main(spawner: Spawner) -> ! { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for RNG + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index e4bac8a5..e0be495d 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -19,7 +19,6 @@ async fn main(_spawner: Spawner) { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for RNG config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index af1d6ebb..1fb4cfec 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -15,7 +15,7 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.hsi48 = true; // needed for RNG. + config.rcc.hsi48 = Some(Default::default()); // needed for RNG let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 19d77183..648ff6ee 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for USB + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 441b00c6..ffede253 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -11,8 +11,7 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let mut config = Config::default(); - config.rcc.hsi48 = true; + let config = Config::default(); let p = embassy_stm32::init(config); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index 61024ef7..8ca9e8b2 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); + config.rcc.hsi = true; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; - config.rcc.hsi48 = true; let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index 9c4f3291..4365c4cf 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -33,8 +33,8 @@ 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.hsi = true; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; - config.rcc.hsi48 = true; let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index 4a50182c..0627ac08 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); + config.rcc.hsi = true; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; - config.rcc.hsi48 = true; let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index f6de6a5c..4f12cadc 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); + config.rcc.hsi = true; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; - config.rcc.hsi48 = true; let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index f76b504a..62caeea5 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -90,7 +90,7 @@ async fn main(spawner: Spawner) { divq: None, divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2) }); - config.rcc.hsi48 = true; // needed for rng + config.rcc.hsi48 = Some(Default::default()); // needed for RNG } let dp = embassy_stm32::init(config); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 15c6f198..d459245d 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.hsi48 = true; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB config.rcc.mux = ClockSrc::PLL1_R; config.rcc.hsi = true; config.rcc.pll = Some(Pll { diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index f59f623b..eaa1c291 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -29,8 +29,7 @@ async fn main(_spawner: Spawner) { n: Plln::MUL10, r: Plldiv::DIV1, }); - //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); - config.rcc.hsi48 = true; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB let p = embassy_stm32::init(config); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 54e23e43..3668e18c 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -306,7 +306,7 @@ pub fn config() -> Config { { use embassy_stm32::rcc::*; config.rcc.hsi = None; - config.rcc.hsi48 = true; // needed for rng + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.hse = Some(Hse { freq: Hertz(8_000_000), mode: HseMode::BypassDigital, @@ -332,7 +332,7 @@ pub fn config() -> Config { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for RNG + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, @@ -364,7 +364,7 @@ pub fn config() -> Config { use embassy_stm32::rcc::*; config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.csi = true; - config.rcc.hsi48 = true; // needed for RNG + config.rcc.hsi48 = Some(Default::default()); // needed for RNG config.rcc.pll1 = Some(Pll { source: PllSource::HSI, prediv: PllPreDiv::DIV4, From 28eb4cd8173063b5e61f371c9642edb5a9289a2b Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 5 Nov 2023 03:37:22 +0000 Subject: [PATCH 05/31] stm32: support internal output on g4 opamps --- embassy-stm32/build.rs | 28 ++-- embassy-stm32/src/opamp.rs | 217 ++++++++++++++++++++++++---- examples/stm32f334/src/bin/opamp.rs | 2 +- 3 files changed, 211 insertions(+), 36 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 276c1d63..35c3f23b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -944,17 +944,25 @@ fn main() { } if regs.kind == "opamp" { - if !pin.signal.starts_with("VP") { - continue; + println!("{}", pin.signal); + + if pin.signal.starts_with("VP") { + // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); + + g.extend(quote! { + impl_opamp_vp_pin!( #peri, #pin_name, #ch); + }) + } else if pin.signal == "VOUT" { + // Impl OutputPin for the VOUT pin + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + g.extend(quote! { + impl_opamp_vout_pin!( #peri, #pin_name ); + }) } - - let peri = format_ident!("{}", p.name); - let pin_name = format_ident!("{}", pin.pin); - let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); - - g.extend(quote! { - impl_opamp_pin!( #peri, #pin_name, #ch); - }) } // DAC is special diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index cb55cbe1..89db6d14 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -13,21 +13,50 @@ pub enum OpAmpGain { Mul16, } -pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin> { - _inner: &'d OpAmp<'d, T>, - _input: &'p mut P, +#[derive(Clone, Copy)] +pub enum OpAmpSpeed { + Normal, + HighSpeed, } +#[cfg(opamp_g4)] +impl From for crate::pac::opamp::vals::OpampCsrOpahsm { + fn from(v: OpAmpSpeed) -> Self { + match v { + OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL, + OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED, + } + } +} + +/// OpAmp external outputs, wired to a GPIO pad. +/// +/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere. +/// +/// This struct can also be used as an ADC input. +pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin> { + _inner: &'d OpAmp<'d, T>, + _output: &'p mut P, +} + +/// OpAmp internal outputs, wired directly to ADC inputs. +/// +/// This struct can be used as an ADC input. +pub struct OpAmpInternalOutput<'d, T: Instance> { + _inner: &'d OpAmp<'d, T>, +} + +/// OpAmp driver. pub struct OpAmp<'d, T: Instance> { _inner: PeripheralRef<'d, T>, } impl<'d, T: Instance> OpAmp<'d, T> { - pub fn new(opamp: impl Peripheral

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

+ 'd) -> Self { + /// Create a new driver instance. + /// + /// Enables the OpAmp and configures the speed, but + /// does not set any other configuration. + pub fn new(opamp: impl Peripheral

+ 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { into_ref!(opamp); #[cfg(opamp_f3)] @@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> { #[cfg(opamp_g4)] T::regs().opamp_csr().modify(|w| { w.set_opaen(true); + w.set_opahsm(speed.into()); }); Self { _inner: opamp } } - pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P> + /// Configure the OpAmp as a buffer for the provided input pin, + /// outputting to the provided output pin. + /// + /// The input pin is configured for analogue mode but not consumed, + /// so it may subsequently be used for ADC or comparator inputs. + /// + /// The output pin is held within the returned [`OpAmpOutput`] struct, + /// preventing it being used elsewhere. The `OpAmpOutput` can then be + /// directly used as an ADC input. + pub fn buffer_ext<'a, 'b, IP, OP>( + &'a mut self, + in_pin: &IP, + out_pin: &'b mut OP, + gain: OpAmpGain, + ) -> OpAmpOutput<'a, 'b, T, OP> where - P: NonInvertingPin, + IP: NonInvertingPin + crate::gpio::sealed::Pin, + OP: OutputPin + crate::gpio::sealed::Pin, { + in_pin.set_as_analog(); + out_pin.set_as_analog(); + let (vm_sel, pga_gain) = match gain { OpAmpGain::Mul1 => (0b11, 0b00), OpAmpGain::Mul2 => (0b10, 0b00), @@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> { #[cfg(opamp_f3)] T::regs().opampcsr().modify(|w| { - w.set_vp_sel(pin.channel()); + w.set_vp_sel(in_pin.channel()); w.set_vm_sel(vm_sel); w.set_pga_gain(pga_gain); + w.set_opampen(true); }); #[cfg(opamp_g4)] T::regs().opamp_csr().modify(|w| { use crate::pac::opamp::vals::*; - w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); + w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel())); w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); + w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN); + w.set_opaen(true); }); OpAmpOutput { _inner: self, - _input: pin, + _output: out_pin, } } + + /// Configure the OpAmp as a buffer for the provided input pin, + /// with the output only used internally. + /// + /// The input pin is configured for analogue mode but not consumed, + /// so it may be subsequently used for ADC or comparator inputs. + /// + /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. + #[cfg(opamp_g4)] + pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T> + where + P: NonInvertingPin + crate::gpio::sealed::Pin, + { + pin.set_as_analog(); + + let (vm_sel, pga_gain) = match gain { + OpAmpGain::Mul1 => (0b11, 0b00), + OpAmpGain::Mul2 => (0b10, 0b00), + OpAmpGain::Mul4 => (0b10, 0b01), + OpAmpGain::Mul8 => (0b10, 0b10), + OpAmpGain::Mul16 => (0b10, 0b11), + }; + + T::regs().opamp_csr().modify(|w| { + use crate::pac::opamp::vals::*; + w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); + w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); + w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); + w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL); + w.set_opaen(true); + }); + + OpAmpInternalOutput { _inner: self } + } +} + +impl<'d, T: Instance> Drop for OpAmp<'d, T> { + fn drop(&mut self) { + #[cfg(opamp_f3)] + T::regs().opampcsr().modify(|w| { + w.set_opampen(false); + }); + + #[cfg(opamp_g4)] + T::regs().opamp_csr().modify(|w| { + w.set_opaen(false); + }); + } } pub trait Instance: sealed::Instance + 'static {} @@ -92,18 +191,19 @@ pub(crate) mod sealed { pub trait InvertingPin { fn channel(&self) -> u8; } + + pub trait OutputPin {} } pub trait NonInvertingPin: sealed::NonInvertingPin {} - pub trait InvertingPin: sealed::InvertingPin {} +pub trait OutputPin: sealed::OutputPin {} -#[cfg(opamp_f3)] -macro_rules! impl_opamp_output { +macro_rules! impl_opamp_external_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d, 'p, P: NonInvertingPin> crate::adc::sealed::AdcPin + impl<'d, 'p, P: OutputPin> crate::adc::sealed::AdcPin for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> { fn channel(&self) -> u8 { @@ -111,7 +211,7 @@ macro_rules! impl_opamp_output { } } - impl<'d, 'p, P: NonInvertingPin> crate::adc::AdcPin + impl<'d, 'p, P: OutputPin> crate::adc::AdcPin for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> { } @@ -120,19 +220,79 @@ macro_rules! impl_opamp_output { }; } -#[cfg(opamp_f3)] foreach_peripheral!( (opamp, OPAMP1) => { - impl_opamp_output!(OPAMP1, ADC1, 3); + impl_opamp_external_output!(OPAMP1, ADC1, 3); }; (opamp, OPAMP2) => { - impl_opamp_output!(OPAMP2, ADC2, 3); + impl_opamp_external_output!(OPAMP2, ADC2, 3); }; (opamp, OPAMP3) => { - impl_opamp_output!(OPAMP3, ADC3, 1); + impl_opamp_external_output!(OPAMP3, ADC3, 1); }; + // OPAMP4 only in STM32G4 Cat 3 devices (opamp, OPAMP4) => { - impl_opamp_output!(OPAMP4, ADC4, 3); + impl_opamp_external_output!(OPAMP4, ADC4, 3); + }; + // OPAMP5 only in STM32G4 Cat 3 devices + (opamp, OPAMP5) => { + impl_opamp_external_output!(OPAMP5, ADC5, 1); + }; + // OPAMP6 only in STM32G4 Cat 3/4 devices + (opamp, OPAMP6) => { + impl_opamp_external_output!(OPAMP6, ADC1, 14); + }; +); + +#[cfg(opamp_g4)] +macro_rules! impl_opamp_internal_output { + ($inst:ident, $adc:ident, $ch:expr) => { + foreach_adc!( + ($adc, $common_inst:ident, $adc_clock:ident) => { + impl<'d> crate::adc::sealed::AdcPin + for OpAmpInternalOutput<'d, crate::peripherals::$inst> + { + fn channel(&self) -> u8 { + $ch + } + } + + impl<'d> crate::adc::AdcPin + for OpAmpInternalOutput<'d, crate::peripherals::$inst> + { + } + }; + ); + }; +} + +#[cfg(opamp_g4)] +foreach_peripheral!( + (opamp, OPAMP1) => { + impl_opamp_internal_output!(OPAMP1, ADC1, 13); + }; + (opamp, OPAMP2) => { + impl_opamp_internal_output!(OPAMP2, ADC2, 16); + }; + (opamp, OPAMP3) => { + impl_opamp_internal_output!(OPAMP3, ADC2, 18); + // Only in Cat 3/4 devices + impl_opamp_internal_output!(OPAMP3, ADC3, 13); + }; + // OPAMP4 only in Cat 3 devices + (opamp, OPAMP4) => { + impl_opamp_internal_output!(OPAMP4, ADC5, 5); + }; + // OPAMP5 only in Cat 3 devices + (opamp, OPAMP5) => { + impl_opamp_internal_output!(OPAMP5, ADC5, 3); + }; + // OPAMP6 only in Cat 3/4 devices + (opamp, OPAMP6) => { + // Only in Cat 3 devices + impl_opamp_internal_output!(OPAMP6, ADC4, 17); + // Only in Cat 4 devices + impl_opamp_internal_output!(OPAMP6, ADC3, 17); }; ); @@ -145,13 +305,12 @@ foreach_peripheral! { } impl Instance for crate::peripherals::$inst { - } }; } #[allow(unused_macros)] -macro_rules! impl_opamp_pin { +macro_rules! impl_opamp_vp_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::opamp::NonInvertingPin for crate::peripherals::$pin {} impl crate::opamp::sealed::NonInvertingPin for crate::peripherals::$pin { @@ -161,3 +320,11 @@ macro_rules! impl_opamp_pin { } }; } + +#[allow(unused_macros)] +macro_rules! impl_opamp_vout_pin { + ($inst:ident, $pin:ident) => { + impl crate::opamp::OutputPin for crate::peripherals::$pin {} + impl crate::opamp::sealed::OutputPin for crate::peripherals::$pin {} + }; +} diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 128001bf..137fc9e6 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! { let mut vrefint = adc.enable_vref(&mut Delay); let mut temperature = adc.enable_temperature(); - let mut buffer = opamp.buffer_for(&mut p.PA7, OpAmpGain::Mul1); + let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1); loop { let vref = adc.read(&mut vrefint).await; From d464d1a841191b79bae6dd7bf9366e7fd076de41 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Mon, 6 Nov 2023 01:11:57 +0000 Subject: [PATCH 06/31] Remove accidentally leftover println --- embassy-stm32/build.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 35c3f23b..965fe4e8 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -944,8 +944,6 @@ fn main() { } if regs.kind == "opamp" { - println!("{}", pin.signal); - if pin.signal.starts_with("VP") { // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc) let peri = format_ident!("{}", p.name); From 239ad5ebea8fb67e8ff2446675343c97687ef81e Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 5 Nov 2023 20:09:33 -0600 Subject: [PATCH 07/31] stm32: update metapac and use stop data --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/build.rs | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 7e11e263..01a9d482 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ 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 } critical-section = "1.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8381654ade324de3945c3c755d359686e957e99b" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1374ed622714ef4702826699ca21cc1f741f4133" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8381654ade324de3945c3c755d359686e957e99b", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1374ed622714ef4702826699ca21cc1f741f4133", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 1307656a..6b41cd39 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -6,7 +6,7 @@ use std::{env, fs}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet}; -use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, METADATA}; +use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA}; fn main() { let target = env::var("TARGET").unwrap(); @@ -557,18 +557,18 @@ fn main() { }; /* - If LP and non-LP peripherals share the same RCC enable bit, then a refcount leak will result. + A refcount leak can result if the same field is shared by peripherals with different stop modes - This should be checked in stm32-data-gen. + This condition should be checked in stm32-data */ - let stop_refcount = if p.name.starts_with("LP") { - quote! { REFCOUNT_STOP2 } - } else { - quote! { REFCOUNT_STOP1 } + let stop_refcount = match rcc.stop_mode { + StopMode::Standby => None, + StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }), + StopMode::Stop1 => Some(quote! { REFCOUNT_STOP1 }), }; - let (incr_stop_refcount, decr_stop_refcount) = if p.name != "RTC" { - ( + let (incr_stop_refcount, decr_stop_refcount) = match stop_refcount { + Some(stop_refcount) => ( quote! { #[cfg(feature = "low-power")] unsafe { crate::rcc::#stop_refcount += 1 }; @@ -577,9 +577,8 @@ fn main() { #[cfg(feature = "low-power")] unsafe { crate::rcc::#stop_refcount -= 1 }; }, - ) - } else { - (quote! {}, quote! {}) + ), + None => (TokenStream::new(), TokenStream::new()), }; g.extend(quote! { From d9b00c01e09deea8d1255a89ce40174960aad793 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Nov 2023 03:26:00 +0100 Subject: [PATCH 08/31] usb: reject instead of panic on CONTROL OUT longer than the buf. --- embassy-usb/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 88d88cad..9fc1e332 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -406,6 +406,16 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { let max_packet_size = self.control.max_packet_size(); let mut total = 0; + if req_length > self.control_buf.len() { + warn!( + "got CONTROL OUT with length {} higher than the control_buf len {}, rejecting.", + req_length, + self.control_buf.len() + ); + self.control.reject().await; + return; + } + let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size); for (first, last, chunk) in first_last(chunks) { let size = match self.control.data_out(chunk, first, last).await { From b8679c0cc85e5eb65bd996ee18deac4a952b1b10 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Nov 2023 03:37:39 +0100 Subject: [PATCH 09/31] stm32/rcc: set highest VOS on some F4s with no overdrive. --- embassy-stm32/src/rcc/f4f7.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/rcc/f4f7.rs b/embassy-stm32/src/rcc/f4f7.rs index 2e4f9572..d507a6fd 100644 --- a/embassy-stm32/src/rcc/f4f7.rs +++ b/embassy-stm32/src/rcc/f4f7.rs @@ -113,6 +113,14 @@ pub(crate) unsafe fn init(config: Config) { while !PWR.csr1().read().odswrdy() {} } + #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423))] + { + use crate::pac::pwr::vals::Vos; + use crate::pac::PWR; + + PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1)); + } + // Configure HSI let hsi = match config.hsi { false => { From 70a700e430b5258b77c678ba66dfb33977f3e7a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Nov 2023 03:38:13 +0100 Subject: [PATCH 10/31] stm32/otg: log TRDT --- embassy-stm32/src/usb_otg/usb.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index e45e4ac4..26a14ff0 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -911,11 +911,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { trace!("enumdne"); let speed = r.dsts().read().enumspd(); - trace!(" speed={}", speed.to_bits()); - - r.gusbcfg().modify(|w| { - w.set_trdt(calculate_trdt(speed, T::frequency())); - }); + let trdt = calculate_trdt(speed, T::frequency()); + trace!(" speed={} trdt={}", speed.to_bits(), trdt); + r.gusbcfg().modify(|w| w.set_trdt(trdt)); r.gintsts().write(|w| w.set_enumdne(true)); // clear Self::restore_irqs(); From b4eef6b1ee5d1b92263ada437692fb7c9c3fe569 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Nov 2023 03:38:42 +0100 Subject: [PATCH 11/31] stm32/otg: fix CONTROL OUT transfers on F4. --- embassy-stm32/src/usb_otg/usb.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 26a14ff0..d90f8343 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -40,6 +40,7 @@ impl interrupt::typelevel::Handler for InterruptHandl // Handle RX while r.gintsts().read().rxflvl() { let status = r.grxstsp().read(); + trace!("=== status {:08x}", status.0); let ep_num = status.epnum() as usize; let len = status.bcnt() as usize; @@ -51,6 +52,15 @@ impl interrupt::typelevel::Handler for InterruptHandl assert!(len == 8, "invalid SETUP packet length={}", len); assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num); + // flushing TX if something stuck in control endpoint + if r.dieptsiz(ep_num).read().pktcnt() != 0 { + r.grstctl().modify(|w| { + w.set_txfnum(ep_num as _); + w.set_txfflsh(true); + }); + while r.grstctl().read().txfflsh() {} + } + 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() }; @@ -96,6 +106,11 @@ impl interrupt::typelevel::Handler for InterruptHandl } vals::Pktstsd::SETUP_DATA_DONE => { trace!("SETUP_DATA_DONE ep={}", ep_num); + + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(ep_num).modify(|w| { + w.set_cnak(true); + }); } x => trace!("unknown PKTSTS: {}", x.to_bits()), } @@ -1312,11 +1327,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { 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 { From 7084570478cafd86e392202598da1550e5cafcaf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Nov 2023 04:13:06 +0100 Subject: [PATCH 12/31] stm32/otg: fix enumeration on non-f4 chips. Fixes regression from #2148 --- embassy-stm32/src/usb_otg/usb.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index d90f8343..ba77bfb1 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -107,10 +107,10 @@ impl interrupt::typelevel::Handler for InterruptHandl vals::Pktstsd::SETUP_DATA_DONE => { trace!("SETUP_DATA_DONE ep={}", ep_num); - // Clear NAK to indicate we are ready to receive more data - T::regs().doepctl(ep_num).modify(|w| { - w.set_cnak(true); - }); + if quirk_setup_late_cnak(r) { + // Clear NAK to indicate we are ready to receive more data + r.doepctl(ep_num).modify(|w| w.set_cnak(true)); + } } x => trace!("unknown PKTSTS: {}", x.to_bits()), } @@ -1317,16 +1317,23 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { state.ep_out_wakers[0].register(cx.waker()); + let r = T::regs(); + 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 // Receive 1 SETUP packet - T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { + r.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 + if !quirk_setup_late_cnak(r) { + r.doepctl(self.ep_out.info.addr.index()).modify(|w| w.set_cnak(true)); + } + trace!("SETUP received: {:?}", data); Poll::Ready(data) } else { @@ -1461,3 +1468,7 @@ fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 { _ => unimplemented!(), } } + +fn quirk_setup_late_cnak(r: crate::pac::otg::Otg) -> bool { + r.cid().read().0 & 0xf000 == 0x1000 +} From 15660cfc68f88b18f4e2fa7a2d776da4ec8b422a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 6 Nov 2023 08:52:11 +0100 Subject: [PATCH 13/31] Ensure TcpIo not blocking when reading into empty slice --- embassy-net/CHANGELOG.md | 4 ++++ embassy-net/src/tcp.rs | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/embassy-net/CHANGELOG.md b/embassy-net/CHANGELOG.md index 4030e050..ada34cb7 100644 --- a/embassy-net/CHANGELOG.md +++ b/embassy-net/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Avoid never resolving `TcpIo::read` when the output buffer is empty. + ## 0.2.1 - 2023-10-31 - Re-add impl_trait_projections diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index b5615cb6..bcd5bb61 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> { // 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) { + // Reading into empty buffer + Ok(0) if buf.is_empty() => { + // embedded_io_async::Read's contract is to not block if buf is empty. While + // this function is not a direct implementor of the trait method, we still don't + // want our future to never resolve. + Poll::Ready(Ok(0)) + } // No data ready Ok(0) => { s.register_recv_waker(cx.waker()); From 8f543062aa8bd44526e864e16deb9765618f191c Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Mon, 6 Nov 2023 18:30:59 +0800 Subject: [PATCH 14/31] check PLL settings before set VOS --- embassy-stm32/src/rcc/f4f7.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/rcc/f4f7.rs b/embassy-stm32/src/rcc/f4f7.rs index d507a6fd..9e8c639d 100644 --- a/embassy-stm32/src/rcc/f4f7.rs +++ b/embassy-stm32/src/rcc/f4f7.rs @@ -1,8 +1,9 @@ +use crate::pac::pwr::vals::Vos; pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; -use crate::pac::{FLASH, RCC}; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -100,12 +101,17 @@ impl Default for Config { } pub(crate) unsafe fn init(config: Config) { + // set VOS to SCALE1, if use PLL + // TODO: check real clock speed before set VOS + if config.pll.is_some() { + PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1)); + } + // always enable overdrive for now. Make it configurable in the future. #[cfg(not(any( stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417 )))] { - use crate::pac::PWR; PWR.cr1().modify(|w| w.set_oden(true)); while !PWR.csr1().read().odrdy() {} @@ -113,14 +119,6 @@ pub(crate) unsafe fn init(config: Config) { while !PWR.csr1().read().odswrdy() {} } - #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423))] - { - use crate::pac::pwr::vals::Vos; - use crate::pac::PWR; - - PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1)); - } - // Configure HSI let hsi = match config.hsi { false => { From 8d8d50cc7a97fa65976e9dc1a82a58de29e7eb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 6 Nov 2023 15:21:53 +0100 Subject: [PATCH 15/31] Yeet core::sync::atomic, remove futures-util dep --- embassy-executor/CHANGELOG.md | 4 ++++ embassy-executor/Cargo.toml | 1 - embassy-executor/src/arch/riscv32.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index cade48f0..ad3e2e95 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,10 @@ 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.3.2 + +- Use `atomic-polyfill` for `riscv32` + ## 0.3.1 - 2023-11-01 - Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies. diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 8e36637a..f59d0f97 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -57,7 +57,6 @@ defmt = { version = "0.3", optional = true } 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.1", path = "../embassy-macros" } embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 40c6877e..e5c0ff2e 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -6,8 +6,8 @@ pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { use core::marker::PhantomData; - use core::sync::atomic::{AtomicBool, Ordering}; + use atomic_polyfill::{AtomicBool, Ordering}; #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; From b8f9341edc721d19e999a76ab4b01c1420200b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 6 Nov 2023 21:50:40 +0100 Subject: [PATCH 16/31] Prepare embassy-executor 0.3.2 --- embassy-executor/CHANGELOG.md | 3 ++- embassy-executor/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index ad3e2e95..e7914cbc 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,9 +5,10 @@ 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.3.2 +## 0.3.2 - 2023-11-06 - Use `atomic-polyfill` for `riscv32` +- Removed unused dependencies (static_cell, futures-util) ## 0.3.1 - 2023-11-01 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index f59d0f97..a85c5dff 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.3.1" +version = "0.3.2" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" From 326bc98bd225a8ec65093e5881e29c57e66fb1cd Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Tue, 7 Nov 2023 02:34:14 +0000 Subject: [PATCH 17/31] Update stm32 usb_raw example to use MSOS descriptors for WinUSB --- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 1 + examples/stm32f4/src/bin/usb_raw.rs | 21 +++++++++++++++++++++ examples/stm32f4/src/bin/usb_serial.rs | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index fca18203..bcf37371 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor"] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } defmt = "0.3" diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 7c0644ae..45dcf56a 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -94,6 +94,7 @@ async fn main(spawner: Spawner) { &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], + &mut [], &mut make_static!([0; 128])[..], ); diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs index 8d4e6c7d..689aea4f 100644 --- a/examples/stm32f4/src/bin/usb_raw.rs +++ b/examples/stm32f4/src/bin/usb_raw.rs @@ -56,10 +56,16 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb_otg::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType}; +use embassy_usb::msos::{self, windows_version}; use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Handler}; use {defmt_rtt as _, panic_probe as _}; +// Randomly generated UUID because Windows requires you provide one to use WinUSB. +// In principle WinUSB-using software could find this device (or a specific interface +// on it) by its GUID instead of using the VID/PID, but in practice that seems unhelpful. +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{DAC2087C-63FA-458D-A55D-827C0762DEC7}"]; + bind_interrupts!(struct Irqs { OTG_FS => usb_otg::InterruptHandler; }); @@ -114,6 +120,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 handler = ControlHandler { @@ -126,9 +133,23 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); + // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor. + // We tell Windows that this entire device is compatible with the "WINUSB" feature, + // which causes it to use the built-in WinUSB driver automatically, which in turn + // can be used by libusb/rusb software without needing a custom driver or INF file. + // In principle you might want to call msos_feature() just on a specific function, + // if your device also has other functions that still use standard class drivers. + builder.msos_descriptor(windows_version::WIN8_1, 0); + builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), + )); + // Add a vendor-specific function (class 0xFF), and corresponding interface, // that uses our custom handler. let mut function = builder.function(0xFF, 0, 0); diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 004ff038..3ab9a6c5 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], &mut control_buf, ); From db4cd73894f13cf63114c42db6b82f0904070ffb Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 09:05:10 +0100 Subject: [PATCH 18/31] rp: Add USB raw example + msos descriptor to examples and usb-logger --- embassy-usb-logger/Cargo.toml | 3 + embassy-usb-logger/src/lib.rs | 6 + examples/rp/Cargo.toml | 4 +- examples/rp/src/bin/usb_ethernet.rs | 1 + examples/rp/src/bin/usb_hid_keyboard.rs | 4 +- examples/rp/src/bin/usb_midi.rs | 1 + examples/rp/src/bin/usb_raw.rs | 149 ++++++++++++++++++++++++ examples/rp/src/bin/usb_serial.rs | 1 + 8 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 examples/rp/src/bin/usb_raw.rs diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 48b8bbcc..02d0ed8e 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -8,6 +8,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-logger-v$VERS src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-logger/src/" target = "thumbv7em-none-eabi" +[features] +msos-descriptor = ["embassy-usb/msos-descriptor"] + [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } embassy-sync = { version = "0.4.0", path = "../embassy-sync" } diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 9178dd6d..95fc0a7e 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -19,6 +19,8 @@ pub struct LoggerState<'d> { device_descriptor: [u8; 32], config_descriptor: [u8; 128], bos_descriptor: [u8; 16], + #[cfg(feature = "msos-descriptor")] + msos_descriptor: [u8; 256], control_buf: [u8; 64], } @@ -30,6 +32,8 @@ impl<'d> LoggerState<'d> { device_descriptor: [0; 32], config_descriptor: [0; 128], bos_descriptor: [0; 16], + #[cfg(feature = "msos-descriptor")] + msos_descriptor: [0; 256], control_buf: [0; 64], } } @@ -73,6 +77,8 @@ impl UsbLogger { &mut state.device_descriptor, &mut state.config_descriptor, &mut state.bos_descriptor, + #[cfg(feature = "msos-descriptor")] + &mut state.msos_descriptor, &mut state.control_buf, ); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index fbe7acae..5ff505e8 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,11 +11,11 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.5", 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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor"] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } +embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger", features = ["msos-descriptor"]} embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } lora-phy = { version = "2" } lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 6c2f27ac..aea9e648 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -71,6 +71,7 @@ async fn main(spawner: Spawner) { &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], + &mut make_static!([0; 0])[..], &mut make_static!([0; 128])[..], ); diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index cc2090d2..569c9b12 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -41,7 +41,7 @@ async fn main(_spawner: Spawner) { 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 msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, - // &mut msos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs index f0b03c81..3ba34c80 100644 --- a/examples/rp/src/bin/usb_midi.rs +++ b/examples/rp/src/bin/usb_midi.rs @@ -58,6 +58,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], &mut control_buf, ); diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs new file mode 100644 index 00000000..e0e5daa5 --- /dev/null +++ b/examples/rp/src/bin/usb_raw.rs @@ -0,0 +1,149 @@ +//! 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)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType}; +use embassy_usb::msos::{self, windows_version}; +use embassy_usb::types::InterfaceNumber; +use embassy_usb::{Builder, Config, Handler}; +use {defmt_rtt as _, panic_probe as _}; + +// This is a randomly generated GUID to allow clients on Windows to find our device +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"]; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[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 driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB raw example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // // 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; + 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 handler = ControlHandler { + if_num: InterfaceNumber(0), + }; + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut msos_descriptor, + &mut control_buf, + ); + + builder.msos_descriptor(windows_version::WIN8_1, 0); + builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), + )); + + // Add a vendor-specific function (class 0xFF), and corresponding interface, + // that uses our custom handler. + let mut function = builder.function(0xFF, 0, 0); + let mut interface = function.interface(); + let _alt = interface.alt_setting(0xFF, 0, 0, None); + handler.if_num = interface.interface_number(); + drop(function); + builder.handler(&mut handler); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + usb.run().await; +} + +/// Handle CONTROL endpoint requests and responses. For many simple requests and responses +/// you can get away with only using the control endpoint. +struct ControlHandler { + if_num: InterfaceNumber, +} + +impl Handler for ControlHandler { + /// Respond to HostToDevice control messages, where the host sends us a command and + /// optionally some data, and we can only acknowledge or reject it. + fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option { + // Log the request before filtering to help with debugging. + info!("Got control_out, request={}, buf={:a}", req, buf); + + // Only handle Vendor request types to an Interface. + if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface { + return None; + } + + // Ignore requests to other interfaces. + if req.index != self.if_num.0 as u16 { + return None; + } + + // Accept request 100, value 200, reject others. + if req.request == 100 && req.value == 200 { + Some(OutResponse::Accepted) + } else { + Some(OutResponse::Rejected) + } + } + + /// Respond to DeviceToHost control messages, where the host requests some data from us. + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + info!("Got control_in, request={}", req); + + // Only handle Vendor request types to an Interface. + if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface { + return None; + } + + // Ignore requests to other interfaces. + if req.index != self.if_num.0 as u16 { + return None; + } + + // Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject. + if req.request == 101 && req.value == 201 && req.length == 5 { + buf[..5].copy_from_slice(b"hello"); + Some(InResponse::Accepted(&buf[..5])) + } else { + Some(InResponse::Rejected) + } + } +} diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 164e2052..0d0317cd 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], &mut control_buf, ); From 50139752bc671c86521e11e0b3872cd64109c65c Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 09:10:18 +0100 Subject: [PATCH 19/31] Add comments --- examples/rp/src/bin/usb_raw.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs index e0e5daa5..044e728a 100644 --- a/examples/rp/src/bin/usb_raw.rs +++ b/examples/rp/src/bin/usb_raw.rs @@ -70,6 +70,12 @@ async fn main(_spawner: Spawner) { &mut control_buf, ); + // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor. + // We tell Windows that this entire device is compatible with the "WINUSB" feature, + // which causes it to use the built-in WinUSB driver automatically, which in turn + // can be used by libusb/rusb software without needing a custom driver or INF file. + // In principle you might want to call msos_feature() just on a specific function, + // if your device also has other functions that still use standard class drivers. builder.msos_descriptor(windows_version::WIN8_1, 0); builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( From 38bfa6916f61f1e633955b311e70da8ecfb81972 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 09:15:21 +0100 Subject: [PATCH 20/31] Update pio-uart example --- examples/rp/src/bin/pio_uart.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 45416c56..dca28d77 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], &mut control_buf, ); From e3fe13e9052e6502b9618b94a64ed69688bbf5b6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 10:58:35 +0100 Subject: [PATCH 21/31] Add docs --- examples/rp/src/bin/usb_raw.rs | 48 ++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs index 044e728a..f59262e5 100644 --- a/examples/rp/src/bin/usb_raw.rs +++ b/examples/rp/src/bin/usb_raw.rs @@ -1,6 +1,50 @@ -//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! Example of using USB without a pre-defined class, but instead responding to +//! raw USB control requests. //! -//! This creates a USB serial port that echos. +//! The host computer can either: +//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer +//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive +//! +//! For higher throughput data, you can add some bulk endpoints after creating the alternate, +//! but for low rate command/response, plain control transfers can be very simple and effective. +//! +//! Example code to send/receive data using `nusb`: +//! +//! ```ignore +//! use futures_lite::future::block_on; +//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient}; +//! +//! fn main() { +//! let di = nusb::list_devices() +//! .unwrap() +//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe) +//! .expect("no device found"); +//! let device = di.open().expect("error opening device"); +//! let interface = device.claim_interface(0).expect("error claiming interface"); +//! +//! // Send "hello world" to device +//! let result = block_on(interface.control_out(ControlOut { +//! control_type: ControlType::Vendor, +//! recipient: Recipient::Interface, +//! request: 100, +//! value: 200, +//! index: 0, +//! data: b"hello world", +//! })); +//! println!("{result:?}"); +//! +//! // Receive "hello" from device +//! let result = block_on(interface.control_in(ControlIn { +//! control_type: ControlType::Vendor, +//! recipient: Recipient::Interface, +//! request: 101, +//! value: 201, +//! index: 0, +//! length: 5, +//! })); +//! println!("{result:?}"); +//! } +//! ``` #![no_std] #![no_main] From d1adc936141b06cb2d8acd2f8a15279307ef06a6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 19:57:05 +0100 Subject: [PATCH 22/31] rp: Add USB raw bulk example --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_raw_bulk.rs | 150 ++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 examples/rp/src/bin/usb_raw_bulk.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 5ff505e8..c5351044 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,6 +12,7 @@ embassy-executor = { version = "0.3.1", path = "../../embassy-executor", feature embassy-time = { version = "0.1.5", 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", "msos-descriptor"] } +embassy-usb-driver = { version = "0.1.0", path = "../../embassy-usb-driver" } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs new file mode 100644 index 00000000..e8618c48 --- /dev/null +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -0,0 +1,150 @@ +//! Example of using USB without a pre-defined class, but instead using raw USB bulk transfers. +//! +//! Example code to send/receive data using `nusb`: +//! +//! ```ignore +//! use futures_lite::future::block_on; +//! use nusb::transfer::RequestBuffer; +//! +//! const BULK_OUT_EP: u8 = 0x01; +//! const BULK_IN_EP: u8 = 0x81; +//! +//! fn main() { +//! let di = nusb::list_devices() +//! .unwrap() +//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe) +//! .expect("no device found"); +//! let device = di.open().expect("error opening device"); +//! let interface = device.claim_interface(0).expect("error claiming interface"); +//! +//! let result = block_on(interface.bulk_out(BULK_OUT_EP, b"hello world".into())); +//! println!("{result:?}"); +//! let result = block_on(interface.bulk_in(BULK_IN_EP, RequestBuffer::new(64))); +//! println!("{result:?}"); +//! } +//! ``` + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_usb::msos::{self, windows_version}; +use embassy_usb::{Builder, Config, Handler}; +use embassy_usb_driver::{Endpoint, EndpointIn, EndpointOut}; +use {defmt_rtt as _, panic_probe as _}; + +// This is a randomly generated GUID to allow clients on Windows to find our device +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"]; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[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 driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB raw example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // // 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; + 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 handler = ControlHandler; + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut msos_descriptor, + &mut control_buf, + ); + + // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor. + // We tell Windows that this entire device is compatible with the "WINUSB" feature, + // which causes it to use the built-in WinUSB driver automatically, which in turn + // can be used by libusb/rusb software without needing a custom driver or INF file. + // In principle you might want to call msos_feature() just on a specific function, + // if your device also has other functions that still use standard class drivers. + builder.msos_descriptor(windows_version::WIN8_1, 0); + builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), + )); + + // Add a vendor-specific function (class 0xFF), and corresponding interface, + // that uses our custom handler. + let mut function = builder.function(0xFF, 0, 0); + let mut interface = function.interface(); + let mut alt = interface.alt_setting(0xFF, 0, 0, None); + let mut read_ep = alt.endpoint_bulk_out(64); + let mut write_ep = alt.endpoint_bulk_in(64); + + drop(function); + builder.handler(&mut handler); + + // 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 { + read_ep.wait_enabled().await; + info!("Connected"); + loop { + let mut data = [0; 64]; + match read_ep.read(&mut data).await { + Ok(n) => { + info!("Got bulk: {:a}", data[..n]); + // Echo back to the host: + write_ep.write(&data[..n]).await.ok(); + } + Err(_) => break, + } + } + 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; +} + +/// Handle CONTROL endpoint requests and responses. +struct ControlHandler; +impl Handler for ControlHandler {} From 37a773c037223573f5bfb3c81c347240af4fa171 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 20:17:19 +0100 Subject: [PATCH 23/31] Use driver reexport --- examples/rp/Cargo.toml | 1 - examples/rp/src/bin/usb_raw_bulk.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c5351044..5ff505e8 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,7 +12,6 @@ embassy-executor = { version = "0.3.1", path = "../../embassy-executor", feature embassy-time = { version = "0.1.5", 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", "msos-descriptor"] } -embassy-usb-driver = { version = "0.1.0", path = "../../embassy-usb-driver" } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index e8618c48..b2274e6c 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -36,7 +36,7 @@ use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_usb::msos::{self, windows_version}; use embassy_usb::{Builder, Config, Handler}; -use embassy_usb_driver::{Endpoint, EndpointIn, EndpointOut}; +use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut}; use {defmt_rtt as _, panic_probe as _}; // This is a randomly generated GUID to allow clients on Windows to find our device From d44383e9a7cfaaa5e5cdbd4b3ade32f5f753616f Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 20:19:56 +0100 Subject: [PATCH 24/31] fmt --- examples/rp/src/bin/usb_raw_bulk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index b2274e6c..ad31045f 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -34,9 +34,9 @@ use embassy_futures::join::join; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut}; use embassy_usb::msos::{self, windows_version}; use embassy_usb::{Builder, Config, Handler}; -use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut}; use {defmt_rtt as _, panic_probe as _}; // This is a randomly generated GUID to allow clients on Windows to find our device From 8effff338302d82b08e2a8f0d97a2d69d9a7b96a Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 7 Nov 2023 20:45:01 +0100 Subject: [PATCH 25/31] rp: Remove control handler from USB raw bulk example --- examples/rp/src/bin/usb_raw_bulk.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index ad31045f..288be5a4 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -36,7 +36,7 @@ use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut}; use embassy_usb::msos::{self, windows_version}; -use embassy_usb::{Builder, Config, Handler}; +use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; // This is a randomly generated GUID to allow clients on Windows to find our device @@ -78,8 +78,6 @@ async fn main(_spawner: Spawner) { let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; - let mut handler = ControlHandler; - let mut builder = Builder::new( driver, config, @@ -110,9 +108,7 @@ async fn main(_spawner: Spawner) { let mut alt = interface.alt_setting(0xFF, 0, 0, None); let mut read_ep = alt.endpoint_bulk_out(64); let mut write_ep = alt.endpoint_bulk_in(64); - drop(function); - builder.handler(&mut handler); // Build the builder. let mut usb = builder.build(); @@ -144,7 +140,3 @@ async fn main(_spawner: Spawner) { // If we had made everything `'static` above instead, we could do this using separate tasks instead. join(usb_fut, echo_fut).await; } - -/// Handle CONTROL endpoint requests and responses. -struct ControlHandler; -impl Handler for ControlHandler {} From 553f0158c0a8abaa293ada3b3f214194418a767e Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 7 Nov 2023 15:39:06 -0600 Subject: [PATCH 26/31] stm32: resolve eth/v2 security bug fixes #2129 --- embassy-stm32/src/eth/v2/descriptors.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index e9799adf..01ea8e57 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs @@ -119,13 +119,11 @@ impl<'a> TDesRing<'a> { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - self.index = self.index + 1; - if self.index == self.descriptors.len() { - self.index = 0; - } - // signal DMA it can try again. - ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) + // See issue #2129 + ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32); + + self.index = (self.index + 1) % self.descriptors.len(); } } @@ -237,21 +235,19 @@ impl<'a> RDesRing<'a> { /// Pop the packet previously returned by `available`. pub(crate) fn pop_packet(&mut self) { - let descriptor = &mut self.descriptors[self.index]; - assert!(descriptor.available()); + let rd = &mut self.descriptors[self.index]; + assert!(rd.available()); - self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + rd.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. - ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0); + // See issue #2129 + ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32); // Increment index. - self.index += 1; - if self.index == self.descriptors.len() { - self.index = 0 - } + self.index = (self.index + 1) % self.descriptors.len(); } } From a3e200d0115d35c710abe28e69addc8ee68a0d2c Mon Sep 17 00:00:00 2001 From: Nigecat Date: Wed, 8 Nov 2023 09:28:33 +1100 Subject: [PATCH 27/31] Fix typo in embassy-net docs --- embassy-net/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/README.md b/embassy-net/README.md index 7bb2283c..52d048e6 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -4,7 +4,7 @@ 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. +memory management designed to work well for embedded systems, aiming for a more "Just Works" experience. ## Features From 79acb560ecb9089e31efdea29c6046225971fee3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Nov 2023 23:05:00 +0100 Subject: [PATCH 28/31] Pin noproto git rev. --- embassy-net-esp-hosted/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index eba54249..2a8c2857 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -15,7 +15,7 @@ embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver- embedded-hal = { version = "1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } -noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } +noproto = { git="https://github.com/embassy-rs/noproto", rev = "c90f3a78d7b5642415e0a07af401320b84d8ab6f", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } heapless = "0.7.16" From 0b015bd727547d1eade5cd12c8b6a1b77483e6db Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Nov 2023 23:08:50 +0100 Subject: [PATCH 29/31] usb: remove msos-descriptor feature. --- embassy-usb-logger/Cargo.toml | 3 --- embassy-usb-logger/src/lib.rs | 3 --- embassy-usb/Cargo.toml | 1 - embassy-usb/src/builder.rs | 18 ++---------------- embassy-usb/src/lib.rs | 23 ++++++----------------- embassy-usb/src/msos.rs | 2 -- examples/nrf52840/Cargo.toml | 2 +- examples/rp/Cargo.toml | 4 ++-- examples/rp/src/bin/pio_uart.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_midi.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/src/bin/usb_serial.rs | 1 + examples/stm32f3/src/bin/usb_serial.rs | 1 + examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 2 +- examples/stm32f4/src/bin/usb_serial.rs | 2 +- examples/stm32f7/src/bin/usb_serial.rs | 1 + examples/stm32g4/src/bin/usb_serial.rs | 1 + examples/stm32h5/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 + 26 files changed, 29 insertions(+), 52 deletions(-) diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 02d0ed8e..48b8bbcc 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -8,9 +8,6 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-logger-v$VERS src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-logger/src/" target = "thumbv7em-none-eabi" -[features] -msos-descriptor = ["embassy-usb/msos-descriptor"] - [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } embassy-sync = { version = "0.4.0", path = "../embassy-sync" } diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 95fc0a7e..45d780bf 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -19,7 +19,6 @@ pub struct LoggerState<'d> { device_descriptor: [u8; 32], config_descriptor: [u8; 128], bos_descriptor: [u8; 16], - #[cfg(feature = "msos-descriptor")] msos_descriptor: [u8; 256], control_buf: [u8; 64], } @@ -32,7 +31,6 @@ impl<'d> LoggerState<'d> { device_descriptor: [0; 32], config_descriptor: [0; 128], bos_descriptor: [0; 16], - #[cfg(feature = "msos-descriptor")] msos_descriptor: [0; 256], control_buf: [0; 64], } @@ -77,7 +75,6 @@ impl UsbLogger { &mut state.device_descriptor, &mut state.config_descriptor, &mut state.bos_descriptor, - #[cfg(feature = "msos-descriptor")] &mut state.msos_descriptor, &mut state.control_buf, ); diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index d820a2d0..5f64b721 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -13,7 +13,6 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] -msos-descriptor = [] default = ["usbd-hid"] # BEGIN AUTOGENERATED CONFIG FEATURES diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index b4ddccd7..c4705d04 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -3,7 +3,6 @@ use heapless::Vec; use crate::config::MAX_HANDLER_COUNT; use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::driver::{Driver, Endpoint, EndpointType}; -#[cfg(feature = "msos-descriptor")] use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::{InterfaceNumber, StringIndex}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; @@ -133,7 +132,6 @@ pub struct Builder<'d, D: Driver<'d>> { config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, - #[cfg(feature = "msos-descriptor")] msos_descriptor: MsOsDescriptorWriter<'d>, } @@ -149,7 +147,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], + msos_descriptor_buf: &'d mut [u8], control_buf: &'d mut [u8], ) -> Self { // Magic values specified in USB-IF ECN on IADs. @@ -189,14 +187,12 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { config_descriptor, bos_descriptor, - #[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(); @@ -206,7 +202,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { info!("USB: device_descriptor used: {}", self.device_descriptor.position()); info!("USB: config_descriptor used: {}", self.config_descriptor.position()); info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); - #[cfg(feature = "msos-descriptor")] info!("USB: msos_descriptor used: {}", msos_descriptor.len()); info!("USB: control_buf size: {}", self.control_buf.len()); @@ -217,10 +212,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.device_descriptor.into_buf(), self.config_descriptor.into_buf(), self.bos_descriptor.writer.into_buf(), + msos_descriptor, self.interfaces, self.control_buf, - #[cfg(feature = "msos-descriptor")] - msos_descriptor, ) } @@ -251,7 +245,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { builder: self, iface_count_index, - #[cfg(feature = "msos-descriptor")] first_interface, } } @@ -275,7 +268,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { StringIndex::new(index) } - #[cfg(feature = "msos-descriptor")] /// Add an MS OS 2.0 Descriptor Set. /// /// Panics if called more than once. @@ -283,13 +275,11 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { 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> { @@ -306,13 +296,11 @@ 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(); } } @@ -344,7 +332,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { } } - #[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() { @@ -355,7 +342,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { self.builder.msos_descriptor.function(self.first_interface); } - #[cfg(feature = "msos-descriptor")] self.builder.msos_descriptor.function_feature(desc); } } diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 9fc1e332..241e33a7 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -175,10 +175,7 @@ pub struct UsbBufferReport { /// 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, + pub msos_descriptor_used: usize, /// Size of the control buffer pub control_buffer_size: usize, } @@ -197,6 +194,7 @@ struct Inner<'d, D: Driver<'d>> { device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], + msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, device_state: UsbDeviceState, suspended: bool, @@ -212,9 +210,6 @@ struct Inner<'d, D: Driver<'d>> { interfaces: Vec, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, - - #[cfg(feature = "msos-descriptor")] - msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -225,9 +220,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], + msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, interfaces: Vec, control_buf: &'d mut [u8], - #[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. @@ -242,6 +237,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { device_descriptor, config_descriptor, bos_descriptor, + msos_descriptor, device_state: UsbDeviceState::Unpowered, suspended: false, @@ -251,8 +247,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { set_address_pending: false, interfaces, handlers, - #[cfg(feature = "msos-descriptor")] - msos_descriptor, }, } } @@ -261,16 +255,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// /// 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, + msos_descriptor_used: self.inner.msos_descriptor.len(), control_buffer_size: self.control_buf.len(), } } @@ -684,7 +673,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } _ => InResponse::Rejected, }, - #[cfg(feature = "msos-descriptor")] + (RequestType::Vendor, Recipient::Device) => { if !self.msos_descriptor.is_empty() && req.request == self.msos_descriptor.vendor_code() diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 13d5d7c4..3858c0f5 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "msos-descriptor")] - //! Microsoft OS Descriptors //! //! diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index f803adb0..7bf904ee 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -34,7 +34,7 @@ embassy-executor = { version = "0.3.1", path = "../../embassy-executor", feature embassy-time = { version = "0.1.5", 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.2.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 } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 5ff505e8..fbe7acae 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,11 +11,11 @@ embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.5", 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", "msos-descriptor"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger", features = ["msos-descriptor"]} +embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } lora-phy = { version = "2" } lorawan-device = { version = "0.11.0", default-features = false, features = ["async", "external-lora-phy"] } diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index dca28d77..fb9d423e 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -75,7 +75,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, - &mut [], + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index aea9e648..cc63029f 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -71,7 +71,7 @@ async fn main(spawner: Spawner) { &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], - &mut make_static!([0; 0])[..], + &mut [], // no msos descriptors &mut make_static!([0; 128])[..], ); diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs index 3ba34c80..d5cdae31 100644 --- a/examples/rp/src/bin/usb_midi.rs +++ b/examples/rp/src/bin/usb_midi.rs @@ -58,7 +58,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, - &mut [], + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 0d0317cd..30347d92 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -60,7 +60,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, - &mut [], + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index 60eb5d0e..31519555 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index a9537c77..d5d068d6 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index bcf37371..f8182190 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } defmt = "0.3" diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 45dcf56a..34407b95 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -94,7 +94,7 @@ async fn main(spawner: Spawner) { &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], - &mut [], + &mut [], // no msos descriptors &mut make_static!([0; 128])[..], ); diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 3ab9a6c5..3e05b0ef 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, - &mut [], + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 2f832c23..6aca732b 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 188988b1..378e7b98 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 13b218d0..7d45818a 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -82,6 +82,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 648ff6ee..f80cf63e 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -78,6 +78,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index d459245d..d977398f 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -72,6 +72,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index f5b3ca34..923193ab 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -82,6 +82,7 @@ async fn main(spawner: Spawner) { &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..], + &mut [], // no msos descriptors &mut make_static!([0; 128])[..], ); diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index bec3d5e4..f64d0f34 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -62,6 +62,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index ff1154f9..58a8898a 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index eaa1c291..a218d5df 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -67,6 +67,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut [], // no msos descriptors &mut control_buf, ); From 4b4c28d8752aab0a96fc244f9eaabe256e7dbeb2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 8 Nov 2023 19:04:20 -0600 Subject: [PATCH 30/31] stm32: add low power for g4 --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/rtc/mod.rs | 162 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/rtc/v2.rs | 138 ----------------------------- embassy-stm32/src/rtc/v3.rs | 8 +- tests/stm32/Cargo.toml | 2 +- 5 files changed, 171 insertions(+), 141 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 01a9d482..7b3a6c2b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -18,7 +18,7 @@ flavors = [ { 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 = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] }, diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index e94a5857..3f03d83a 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -33,6 +33,60 @@ use embassy_hal_internal::Peripheral; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub(crate) enum WakeupPrescaler { + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, +} + +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +impl From for crate::pac::rtc::vals::Wucksel { + fn from(val: WakeupPrescaler) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + WakeupPrescaler::Div2 => Wucksel::DIV2, + WakeupPrescaler::Div4 => Wucksel::DIV4, + WakeupPrescaler::Div8 => Wucksel::DIV8, + WakeupPrescaler::Div16 => Wucksel::DIV16, + } + } +} + +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +impl From for WakeupPrescaler { + fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + Wucksel::DIV2 => WakeupPrescaler::Div2, + Wucksel::DIV4 => WakeupPrescaler::Div4, + Wucksel::DIV8 => WakeupPrescaler::Div8, + Wucksel::DIV16 => WakeupPrescaler::Div16, + _ => unreachable!(), + } + } +} + +#[cfg(feature = "low-power")] +impl WakeupPrescaler { + pub fn compute_min(val: u32) -> Self { + *[ + WakeupPrescaler::Div2, + WakeupPrescaler::Div4, + WakeupPrescaler::Div8, + WakeupPrescaler::Div16, + ] + .iter() + .skip_while(|psc| **psc as u32 <= val) + .next() + .unwrap_or(&WakeupPrescaler::Div16) + } +} + /// Errors that can occur on methods on [RtcClock] #[non_exhaustive] #[derive(Clone, Debug, PartialEq, Eq)] @@ -277,6 +331,114 @@ impl Rtc { pub fn write_backup_register(&self, register: usize, value: u32) { RTC::write_backup_register(&RTC::regs(), register, value) } + + #[cfg(feature = "low-power")] + /// start the wakeup alarm and wtih a duration that is as close to but less than + /// the requested duration, and record the instant the wakeup alarm was started + pub(crate) fn start_wakeup_alarm( + &self, + requested_duration: embassy_time::Duration, + cs: critical_section::CriticalSection, + ) { + use embassy_time::{Duration, TICK_HZ}; + + #[cfg(any(rtc_v3, rtc_v3u5))] + use crate::pac::rtc::vals::Calrf; + + // Panic if the rcc mod knows we're not using low-power rtc + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); + + let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); + let rtc_hz = Self::frequency().0 as u64; + let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; + let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); + + // adjust the rtc ticks to the prescaler and subtract one rtc tick + let rtc_ticks = rtc_ticks / prescaler as u64; + let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + { + regs.isr().modify(|w| w.set_wutf(false)); + while !regs.isr().read().wutwf() {} + } + + #[cfg(any(rtc_v3, rtc_v3u5))] + { + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + while !regs.icsr().read().wutwf() {} + } + + regs.cr().modify(|w| w.set_wucksel(prescaler.into())); + regs.wutr().write(|w| w.set_wut(rtc_ticks)); + regs.cr().modify(|w| w.set_wute(true)); + regs.cr().modify(|w| w.set_wutie(true)); + }); + + let instant = self.instant().unwrap(); + trace!( + "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", + Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), + prescaler as u32, + rtc_ticks, + instant, + ); + + assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) + } + + #[cfg(feature = "low-power")] + /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` + /// was called, otherwise none + pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { + use crate::interrupt::typelevel::Interrupt; + #[cfg(any(rtc_v3, rtc_v3u5))] + use crate::pac::rtc::vals::Calrf; + + let instant = self.instant().unwrap(); + if RTC::regs().cr().read().wute() { + trace!("rtc: stop wakeup alarm at {}", instant); + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + regs.isr().modify(|w| w.set_wutf(false)); + + #[cfg(any(rtc_v3, rtc_v3u5))] + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + + crate::pac::EXTI + .pr(0) + .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + + ::WakeupInterrupt::unpend(); + }); + } + + self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) + } + + #[cfg(feature = "low-power")] + pub(crate) fn enable_wakeup_line(&self) { + use crate::interrupt::typelevel::Interrupt; + use crate::pac::EXTI; + + ::WakeupInterrupt::unpend(); + unsafe { ::WakeupInterrupt::enable() }; + + EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + } } pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 006fd63f..91f08fae 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -6,145 +6,7 @@ use crate::peripherals::RTC; use crate::rtc::sealed::Instance; #[allow(dead_code)] -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub(crate) enum WakeupPrescaler { - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, -} - -#[cfg(any(stm32wb, stm32f4, stm32l0))] -impl From for crate::pac::rtc::vals::Wucksel { - fn from(val: WakeupPrescaler) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - WakeupPrescaler::Div2 => Wucksel::DIV2, - WakeupPrescaler::Div4 => Wucksel::DIV4, - WakeupPrescaler::Div8 => Wucksel::DIV8, - WakeupPrescaler::Div16 => Wucksel::DIV16, - } - } -} - -#[cfg(any(stm32wb, stm32f4, stm32l0))] -impl From for WakeupPrescaler { - fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - Wucksel::DIV2 => WakeupPrescaler::Div2, - Wucksel::DIV4 => WakeupPrescaler::Div4, - Wucksel::DIV8 => WakeupPrescaler::Div8, - Wucksel::DIV16 => WakeupPrescaler::Div16, - _ => unreachable!(), - } - } -} - -#[allow(dead_code)] -impl WakeupPrescaler { - pub fn compute_min(val: u32) -> Self { - *[ - WakeupPrescaler::Div2, - WakeupPrescaler::Div4, - WakeupPrescaler::Div8, - WakeupPrescaler::Div16, - ] - .iter() - .skip_while(|psc| **psc as u32 <= val) - .next() - .unwrap_or(&WakeupPrescaler::Div16) - } -} - impl super::Rtc { - #[cfg(feature = "low-power")] - /// start the wakeup alarm and wtih a duration that is as close to but less than - /// the requested duration, and record the instant the wakeup alarm was started - pub(crate) fn start_wakeup_alarm( - &self, - requested_duration: embassy_time::Duration, - cs: critical_section::CriticalSection, - ) { - use embassy_time::{Duration, TICK_HZ}; - - // Panic if the rcc mod knows we're not using low-power rtc - #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] - unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); - - let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); - let rtc_hz = Self::frequency().0 as u64; - let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; - let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); - - // adjust the rtc ticks to the prescaler and subtract one rtc tick - let rtc_ticks = rtc_ticks / prescaler as u64; - let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wute(false)); - regs.isr().modify(|w| w.set_wutf(false)); - while !regs.isr().read().wutwf() {} - - regs.cr().modify(|w| w.set_wucksel(prescaler.into())); - regs.wutr().write(|w| w.set_wut(rtc_ticks)); - regs.cr().modify(|w| w.set_wute(true)); - regs.cr().modify(|w| w.set_wutie(true)); - }); - - let instant = self.instant().unwrap(); - trace!( - "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", - Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), - prescaler as u32, - rtc_ticks, - instant, - ); - - assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) - } - - #[cfg(feature = "low-power")] - /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` - /// was called, otherwise none - pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { - use crate::interrupt::typelevel::Interrupt; - - let instant = self.instant().unwrap(); - if RTC::regs().cr().read().wute() { - trace!("rtc: stop wakeup alarm at {}", instant); - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(false)); - regs.cr().modify(|w| w.set_wute(false)); - regs.isr().modify(|w| w.set_wutf(false)); - - crate::pac::EXTI - .pr(0) - .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - - ::WakeupInterrupt::unpend(); - }); - } - - self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) - } - - #[cfg(feature = "low-power")] - pub(crate) fn enable_wakeup_line(&self) { - use crate::interrupt::typelevel::Interrupt; - use crate::pac::EXTI; - - ::WakeupInterrupt::unpend(); - unsafe { ::WakeupInterrupt::enable() }; - - EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - } - /// Applies the RTC config /// It this changes the RTC clock source the time will be reset pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7bf757e7..d2d0d930 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -95,7 +95,7 @@ impl super::Rtc { }) } - pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + pub(super) fn write(&self, init_mode: bool, f: F) -> R where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { @@ -129,6 +129,12 @@ impl super::Rtc { impl sealed::Instance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 32; + #[cfg(all(feature = "low-power", stm32g4))] + const EXTI_WAKEUP_LINE: usize = 20; + + #[cfg(all(feature = "low-power", stm32g4))] + type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; + fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 4b769b1c..9b9273a5 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -10,7 +10,7 @@ stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] -stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "not-gpdma", "rng"] +stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"] From 05a4bb3a4acdb5a980efbb6f807696c1c702e01c Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 8 Nov 2023 19:06:29 -0600 Subject: [PATCH 31/31] rustfmt --- embassy-stm32/src/rtc/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 3f03d83a..b4315f53 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -33,6 +33,7 @@ use embassy_hal_internal::Peripheral; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; +#[allow(dead_code)] #[repr(u8)] #[derive(Clone, Copy, Debug)] pub(crate) enum WakeupPrescaler {