From d3e08ba2bab5942d41949888255bdbcd7d54eb90 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 2 Feb 2025 23:52:43 +0100 Subject: [PATCH 1/8] Enable defmt features in deps --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index bb695d0..b133112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,12 @@ log-itm = ["log"] log-rtt = ["log"] log-semihost = ["log"] +defmt = [ + "dep:defmt", + "fugit/defmt", + "stm32h5/defmt", +] + [dependencies] cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] } stm32h5 = { package = "stm32h5", version = "0.16.0" } From b6c7432d0643598ceb9ebf7491135401097f02e7 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 2 Feb 2025 18:55:42 +0100 Subject: [PATCH 2/8] Implement USB support, inspiration taken from G4, F3, F4 and H7 hals --- Cargo.toml | 3 ++ examples/usb_serial.rs | 100 +++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/prelude.rs | 1 + src/usb.rs | 116 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 examples/usb_serial.rs create mode 100644 src/usb.rs diff --git a/Cargo.toml b/Cargo.toml index b133112..a0a81af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ embedded-hal = "1.0.0" defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} +stm32-usbd = "0.8.0" [dev-dependencies] log = { version = "0.4.20"} @@ -85,6 +86,8 @@ cortex-m-semihosting = "0.5.0" panic-itm = { version = "~0.4.1" } panic-probe = "0.3.2" panic-semihosting = "0.6" +usbd-serial = "0.2.2" +usb-device = { version = "0.3.2", features = ["defmt", "log"] } [profile.release] codegen-units = 1 # better optimizations diff --git a/examples/usb_serial.rs b/examples/usb_serial.rs new file mode 100644 index 0000000..0686139 --- /dev/null +++ b/examples/usb_serial.rs @@ -0,0 +1,100 @@ +//! CDC-ACM serial port example using polling in a busy loop. +#![deny(warnings)] +#![deny(unsafe_code)] +#![allow(clippy::uninlined_format_args)] +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use hal::prelude::*; +use hal::pwr::PwrExt; +use hal::stm32; +use stm32_usbd::UsbBus; +use stm32h5xx_hal as hal; +use stm32h5xx_hal::usb::UsbExt; + +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +#[macro_use] +mod utilities; + +use utilities::logger::info; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.vos0().freeze(); + // Constrain and Freeze clock + let rcc = dp.RCC.constrain(); + let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS); + + let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); + + let mut led = gpioa.pa5.into_push_pull_output(); + led.set_low(); + + let usb_dm = gpioa.pa11.into_alternate(); + let usb_dp = gpioa.pa12.into_alternate(); + + let usb = dp.USB.usb(ccdr.peripheral.USB, usb_dm, usb_dp); + let usb_bus = UsbBus::new(usb); + + let mut serial = SerialPort::new(&usb_bus); + + let mut usb_dev = + UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .strings(&[StringDescriptors::default() + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST")]) + .unwrap() + .device_class(USB_CLASS_CDC) + .build(); + + info!("Init done"); + + loop { + if !usb_dev.poll(&mut [&mut serial]) { + continue; + } + + let mut buf = [0u8; 64]; + + match serial.read(&mut buf) { + Ok(count) if count > 0 => { + led.set_high(); + + if let Ok(s) = str::from_utf8(&buf[0..count]) { + info!("{:?}", s); + } else { + info!("{:?}", &buf[0..count]); + } + + // Echo back in upper case + for c in buf[0..count].iter_mut() { + if 0x61 <= *c && *c <= 0x7a { + *c &= !0x20; + } + } + + let mut write_offset = 0; + while write_offset < count { + match serial.write(&buf[write_offset..count]) { + Ok(len) if len > 0 => { + write_offset += len; + } + _ => {} + } + } + } + _ => {} + } + + led.set_low(); + } +} diff --git a/src/lib.rs b/src/lib.rs index ed30976..3c27058 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,9 @@ pub mod spi; #[cfg(feature = "device-selected")] pub mod dwt; +#[cfg(feature = "device-selected")] +pub mod usb; + #[cfg(feature = "device-selected")] mod sealed { pub trait Sealed {} diff --git a/src/prelude.rs b/src/prelude.rs index 3a6b735..becb02a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,6 +8,7 @@ pub use crate::icache::ICacheExt as _stm32h5xx_hal_icache_ICacheExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt; pub use crate::spi::SpiExt as _stm32h5xx_hal_spi_SpiExt; +pub use crate::usb::UsbExt as _stm32h5xx_hal_usb_UsbExt; pub use crate::time::U32Ext as _; pub use fugit::{ExtU32 as _, RateExtU32 as _}; diff --git a/src/usb.rs b/src/usb.rs new file mode 100644 index 0000000..5c735b5 --- /dev/null +++ b/src/usb.rs @@ -0,0 +1,116 @@ +//! USB peripheral. +//! +//! Provides the required implementation for use of the [`stm32-usbd`] crate. + +use crate::stm32::rcc::ccipr4::USBSEL; +pub use stm32_usbd::UsbBus; + +use crate::gpio; +use crate::gpio::gpioa::{PA11, PA12}; +use crate::rcc::rec; +use crate::stm32::{self, USB}; +use core::fmt; +use stm32_usbd::UsbPeripheral; + +/// Type for pin that can be the "D-" pin for the USB peripheral +pub type DmPin = PA11>; + +/// Type for pin that can be the "D+" pin for the USB peripheral +pub type DpPin = PA12>; + +pub trait UsbExt { + fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> Peripheral; +} + +impl UsbExt for stm32::USB { + fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> Peripheral { + if let USBSEL::Disable = rec.get_kernel_clk_mux() { + rec.kernel_clk_mux(USBSEL::Hsi48); + }; + + Peripheral { + _usb: self, + pin_dm, + pin_dp, + } + } +} + +pub struct Peripheral { + /// USB register block + _usb: USB, + /// Data negative pin + pin_dm: DmPin, + /// Data positive pin + pin_dp: DpPin, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Peripheral { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "Peripheral {{ usb: USB, pin_dm: {}, pin_dp: {}}}", + self.pin_dm, + self.pin_dp + ); + } +} + +impl fmt::Debug for Peripheral { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Peripheral") + .field("usb", &"USB") + .field("pin_dm", &self.pin_dm) + .field("pin_dp", &self.pin_dp) + .finish() + } +} + +// SAFETY: Implementation of Peripheral is thread-safe by using cricitcal sections to ensure +// mutually exclusive access to the USB peripheral +unsafe impl Sync for Peripheral {} + +// SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS +unsafe impl UsbPeripheral for Peripheral { + const REGISTERS: *const () = USB::ptr().cast::<()>(); + const DP_PULL_UP_FEATURE: bool = true; + const EP_MEMORY: *const () = 0x4001_6400 as _; + const EP_MEMORY_SIZE: usize = 2048; + const EP_MEMORY_ACCESS: stm32_usbd::MemoryAccess = + stm32_usbd::MemoryAccess::Word32x1; + + fn enable() { + cortex_m::interrupt::free(|_| { + let rcc = unsafe { &*stm32::RCC::ptr() }; + + #[cfg(any(feature = "h523_h533", feature = "h56x_h573"))] + { + let pwr = unsafe { &*stm32::PWR::ptr() }; + + // Enable USB supply level detector + pwr.usbscr().modify(|_, w| w.usb33den().set_bit()); + + // Await good usb supply voltage + while pwr.vmsr().read().usb33rdy().bit_is_clear() {} + + // Set bit to confirm that USB supply level is good + pwr.usbscr().modify(|_, w| w.usb33sv().set_bit()); + } + + // Enable USB peripheral + rcc.apb2enr().modify(|_, w| w.usben().set_bit()); + + // Reset USB peripheral + rcc.apb2rstr().modify(|_, w| w.usbrst().set_bit()); + rcc.apb2rstr().modify(|_, w| w.usbrst().clear_bit()); + }); + } + + fn startup_delay() { + // There is a chip specific startup delay. For STM32H503,523,533,56x and 573 it's + // 1µs and this should wait for at least that long. + // 250 Mhz is the highest frequency, so this ensures a minimum of 1µs wait time. + cortex_m::asm::delay(250); + } +} From 2b163b7e6ee92f6f123747dc775b31fe048d5f45 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Jul 2025 20:40:58 +0200 Subject: [PATCH 3/8] Bump MSRV to 1.88 --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a615547..2cdfcc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: # All permutations of {rust, mcu} rust: - - 1.78.0 # MSRV + - 1.88.0 # MSRV - stable mcu: - stm32h503 diff --git a/Cargo.toml b/Cargo.toml index a0a81af..da813f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Edwin Svensson "] homepage = "https://github.com/stm32-rs/stm32h5xx-hal" repository = "https://github.com/stm32-rs/stm32h5xx-hal" readme = "README.md" -rust-version = "1.78.0" +rust-version = "1.88.0" categories = ["embedded", "hardware-support", "no-std"] description = "Hardware Abstraction Layer implementation for STM32H5 series microcontrollers" keywords = ["arm", "cortex-m", "stm32h5xx", "hal", "embedded-hal"] From 2f23ba39ecf467896f516f2ddf2e63380055daac Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Jul 2025 20:42:51 +0200 Subject: [PATCH 4/8] Bump MSRV to 1.88 in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4465541..f1b7473 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![docs.rs](https://docs.rs/stm32h5xx-hal/badge.svg)](https://docs.rs/stm32h5xx-hal) [![CI](https://github.com/stm32-rs/stm32h5xx-hal/workflows/Continuous%20integration/badge.svg)](https://github.com/stm32-rs/stm32h5xx-hal/actions) [![Crates.io](https://img.shields.io/crates/v/stm32h5xx-hal.svg)](https://crates.io/crates/stm32h5xx-hal) -![Minimum rustc version](https://img.shields.io/badge/rustc-1.78.0+-yellow.svg) +![Minimum rustc version](https://img.shields.io/badge/rustc-1.88.0+-yellow.svg) [_stm32h5xx-hal_](https://github.com/stm32-rs/stm32h5xx-hal) contains a hardware abstraction layer on top of the peripheral access API for @@ -50,7 +50,7 @@ of support for peripherals is shown in the table below. ## Minimum supported Rust version -The Minimum Supported Rust Version (MSRV) at the moment is **1.78.0**. Older +The Minimum Supported Rust Version (MSRV) at the moment is **1.88.0**. Older versions **may** compile, especially when some features are not used in your application. From 1a5fe49c7fe2e0971051421de6dc385f777cc2c2 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Jul 2025 20:47:33 +0200 Subject: [PATCH 5/8] Use to_ascii_uppercase in USB example --- examples/usb_serial.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/usb_serial.rs b/examples/usb_serial.rs index 0686139..dc6e04e 100644 --- a/examples/usb_serial.rs +++ b/examples/usb_serial.rs @@ -76,11 +76,9 @@ fn main() -> ! { } // Echo back in upper case - for c in buf[0..count].iter_mut() { - if 0x61 <= *c && *c <= 0x7a { - *c &= !0x20; - } - } + buf[0..count] + .iter_mut() + .for_each(|c| *c = c.to_ascii_uppercase()); let mut write_offset = 0; while write_offset < count { From 02f00dc975d0f67e0554a0cc33c7cd078baac966 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Jul 2025 23:25:31 +0200 Subject: [PATCH 6/8] Address comments --- src/usb.rs | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/usb.rs b/src/usb.rs index 5c735b5..9e776b4 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -7,9 +7,10 @@ pub use stm32_usbd::UsbBus; use crate::gpio; use crate::gpio::gpioa::{PA11, PA12}; -use crate::rcc::rec; +use crate::rcc::{rec, ResetEnable}; use crate::stm32::{self, USB}; use core::fmt; +use core::marker::PhantomData; use stm32_usbd::UsbPeripheral; /// Type for pin that can be the "D-" pin for the USB peripheral @@ -19,16 +20,16 @@ pub type DmPin = PA11>; pub type DpPin = PA12>; pub trait UsbExt { - fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> Peripheral; + fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> UsbDevice; } impl UsbExt for stm32::USB { - fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> Peripheral { + fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> UsbDevice { if let USBSEL::Disable = rec.get_kernel_clk_mux() { rec.kernel_clk_mux(USBSEL::Hsi48); }; - Peripheral { + UsbDevice { _usb: self, pin_dm, pin_dp, @@ -36,7 +37,7 @@ impl UsbExt for stm32::USB { } } -pub struct Peripheral { +pub struct UsbDevice { /// USB register block _usb: USB, /// Data negative pin @@ -46,7 +47,7 @@ pub struct Peripheral { } #[cfg(feature = "defmt")] -impl defmt::Format for Peripheral { +impl defmt::Format for UsbDevice { fn format(&self, f: defmt::Formatter) { defmt::write!( f, @@ -57,7 +58,7 @@ impl defmt::Format for Peripheral { } } -impl fmt::Debug for Peripheral { +impl fmt::Debug for UsbDevice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Peripheral") .field("usb", &"USB") @@ -69,10 +70,10 @@ impl fmt::Debug for Peripheral { // SAFETY: Implementation of Peripheral is thread-safe by using cricitcal sections to ensure // mutually exclusive access to the USB peripheral -unsafe impl Sync for Peripheral {} +unsafe impl Sync for UsbDevice {} // SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS -unsafe impl UsbPeripheral for Peripheral { +unsafe impl UsbPeripheral for UsbDevice { const REGISTERS: *const () = USB::ptr().cast::<()>(); const DP_PULL_UP_FEATURE: bool = true; const EP_MEMORY: *const () = 0x4001_6400 as _; @@ -82,10 +83,11 @@ unsafe impl UsbPeripheral for Peripheral { fn enable() { cortex_m::interrupt::free(|_| { - let rcc = unsafe { &*stm32::RCC::ptr() }; - #[cfg(any(feature = "h523_h533", feature = "h56x_h573"))] { + // Safety: we are only touching the usbscr which + // is specific for this peripheral. This together with + // the critical section unsures exclusive access let pwr = unsafe { &*stm32::PWR::ptr() }; // Enable USB supply level detector @@ -98,12 +100,12 @@ unsafe impl UsbPeripheral for Peripheral { pwr.usbscr().modify(|_, w| w.usb33sv().set_bit()); } - // Enable USB peripheral - rcc.apb2enr().modify(|_, w| w.usben().set_bit()); - - // Reset USB peripheral - rcc.apb2rstr().modify(|_, w| w.usbrst().set_bit()); - rcc.apb2rstr().modify(|_, w| w.usbrst().clear_bit()); + // Reset and enable USB peripheral + rec::Usb { + _marker: PhantomData, + } + .reset() + .enable(); }); } From bc669c3b087959b5b9c02a89cc16e9061cda32b5 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 29 Jul 2025 02:26:35 +0200 Subject: [PATCH 7/8] Fix more comments --- src/usb.rs | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/src/usb.rs b/src/usb.rs index 9e776b4..b90ff50 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -9,7 +9,6 @@ use crate::gpio; use crate::gpio::gpioa::{PA11, PA12}; use crate::rcc::{rec, ResetEnable}; use crate::stm32::{self, USB}; -use core::fmt; use core::marker::PhantomData; use stm32_usbd::UsbPeripheral; @@ -24,51 +23,23 @@ pub trait UsbExt { } impl UsbExt for stm32::USB { - fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> UsbDevice { + fn usb(self, rec: rec::Usb, _pin_dm: DmPin, _pin_dp: DpPin) -> UsbDevice { if let USBSEL::Disable = rec.get_kernel_clk_mux() { rec.kernel_clk_mux(USBSEL::Hsi48); }; - UsbDevice { - _usb: self, - pin_dm, - pin_dp, - } + UsbDevice { _usb: self } } } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug)] pub struct UsbDevice { /// USB register block _usb: USB, - /// Data negative pin - pin_dm: DmPin, - /// Data positive pin - pin_dp: DpPin, } -#[cfg(feature = "defmt")] -impl defmt::Format for UsbDevice { - fn format(&self, f: defmt::Formatter) { - defmt::write!( - f, - "Peripheral {{ usb: USB, pin_dm: {}, pin_dp: {}}}", - self.pin_dm, - self.pin_dp - ); - } -} - -impl fmt::Debug for UsbDevice { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Peripheral") - .field("usb", &"USB") - .field("pin_dm", &self.pin_dm) - .field("pin_dp", &self.pin_dp) - .finish() - } -} - -// SAFETY: Implementation of Peripheral is thread-safe by using cricitcal sections to ensure +// SAFETY: Implementation of UsbDevice is thread-safe by using cricitcal sections to ensure // mutually exclusive access to the USB peripheral unsafe impl Sync for UsbDevice {} From 75ed45bcb44fa65e75b25995450a2ad4958ae275 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 29 Jul 2025 02:29:55 +0200 Subject: [PATCH 8/8] Manual defmt::Format impl --- src/usb.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/usb.rs b/src/usb.rs index b90ff50..79ad62c 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -32,13 +32,19 @@ impl UsbExt for stm32::USB { } } -#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug)] pub struct UsbDevice { /// USB register block _usb: USB, } +#[cfg(feature = "defmt")] +impl defmt::Format for UsbDevice { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "UsbDevice {{ usb: USB }}"); + } +} + // SAFETY: Implementation of UsbDevice is thread-safe by using cricitcal sections to ensure // mutually exclusive access to the USB peripheral unsafe impl Sync for UsbDevice {}