Skip to content

Commit b82da58

Browse files
authored
usb: Integrate stm32-usbd to provide USB functionality (#63)
1 parent bcd0425 commit b82da58

File tree

7 files changed

+210
-4
lines changed

7 files changed

+210
-4
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
strategy:
1212
matrix: # All permutations of {rust, mcu}
1313
rust:
14-
- 1.78.0 # MSRV
14+
- 1.88.0 # MSRV
1515
- stable
1616
mcu:
1717
- stm32h503

Cargo.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = ["Edwin Svensson <[email protected]>"]
66
homepage = "https://github.com/stm32-rs/stm32h5xx-hal"
77
repository = "https://github.com/stm32-rs/stm32h5xx-hal"
88
readme = "README.md"
9-
rust-version = "1.78.0"
9+
rust-version = "1.88.0"
1010
categories = ["embedded", "hardware-support", "no-std"]
1111
description = "Hardware Abstraction Layer implementation for STM32H5 series microcontrollers"
1212
keywords = ["arm", "cortex-m", "stm32h5xx", "hal", "embedded-hal"]
@@ -56,6 +56,12 @@ log-itm = ["log"]
5656
log-rtt = ["log"]
5757
log-semihost = ["log"]
5858

59+
defmt = [
60+
"dep:defmt",
61+
"fugit/defmt",
62+
"stm32h5/defmt",
63+
]
64+
5965
[dependencies]
6066
cortex-m = { version = "^0.7.7", features = ["critical-section-single-core"] }
6167
stm32h5 = { package = "stm32h5", version = "0.16.0" }
@@ -64,6 +70,7 @@ embedded-hal = "1.0.0"
6470
defmt = { version = "1.0.0", optional = true }
6571
paste = "1.0.15"
6672
log = { version = "0.4.20", optional = true}
73+
stm32-usbd = "0.8.0"
6774

6875
[dev-dependencies]
6976
log = { version = "0.4.20"}
@@ -79,6 +86,8 @@ cortex-m-semihosting = "0.5.0"
7986
panic-itm = { version = "~0.4.1" }
8087
panic-probe = "0.3.2"
8188
panic-semihosting = "0.6"
89+
usbd-serial = "0.2.2"
90+
usb-device = { version = "0.3.2", features = ["defmt", "log"] }
8291

8392
[profile.release]
8493
codegen-units = 1 # better optimizations

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![docs.rs](https://docs.rs/stm32h5xx-hal/badge.svg)](https://docs.rs/stm32h5xx-hal)
66
[![CI](https://github.com/stm32-rs/stm32h5xx-hal/workflows/Continuous%20integration/badge.svg)](https://github.com/stm32-rs/stm32h5xx-hal/actions)
77
[![Crates.io](https://img.shields.io/crates/v/stm32h5xx-hal.svg)](https://crates.io/crates/stm32h5xx-hal)
8-
![Minimum rustc version](https://img.shields.io/badge/rustc-1.78.0+-yellow.svg)
8+
![Minimum rustc version](https://img.shields.io/badge/rustc-1.88.0+-yellow.svg)
99

1010
[_stm32h5xx-hal_](https://github.com/stm32-rs/stm32h5xx-hal) contains
1111
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.
5050

5151
## Minimum supported Rust version
5252

53-
The Minimum Supported Rust Version (MSRV) at the moment is **1.78.0**. Older
53+
The Minimum Supported Rust Version (MSRV) at the moment is **1.88.0**. Older
5454
versions **may** compile, especially when some features are not used in your
5555
application.
5656

examples/usb_serial.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! CDC-ACM serial port example using polling in a busy loop.
2+
#![deny(warnings)]
3+
#![deny(unsafe_code)]
4+
#![allow(clippy::uninlined_format_args)]
5+
#![no_std]
6+
#![no_main]
7+
8+
use cortex_m_rt::entry;
9+
use hal::prelude::*;
10+
use hal::pwr::PwrExt;
11+
use hal::stm32;
12+
use stm32_usbd::UsbBus;
13+
use stm32h5xx_hal as hal;
14+
use stm32h5xx_hal::usb::UsbExt;
15+
16+
use usb_device::prelude::*;
17+
use usbd_serial::{SerialPort, USB_CLASS_CDC};
18+
19+
#[macro_use]
20+
mod utilities;
21+
22+
use utilities::logger::info;
23+
24+
#[entry]
25+
fn main() -> ! {
26+
utilities::logger::init();
27+
28+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
29+
30+
let pwr = dp.PWR.constrain();
31+
let pwrcfg = pwr.vos0().freeze();
32+
// Constrain and Freeze clock
33+
let rcc = dp.RCC.constrain();
34+
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);
35+
36+
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
37+
38+
let mut led = gpioa.pa5.into_push_pull_output();
39+
led.set_low();
40+
41+
let usb_dm = gpioa.pa11.into_alternate();
42+
let usb_dp = gpioa.pa12.into_alternate();
43+
44+
let usb = dp.USB.usb(ccdr.peripheral.USB, usb_dm, usb_dp);
45+
let usb_bus = UsbBus::new(usb);
46+
47+
let mut serial = SerialPort::new(&usb_bus);
48+
49+
let mut usb_dev =
50+
UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
51+
.strings(&[StringDescriptors::default()
52+
.manufacturer("Fake company")
53+
.product("Serial port")
54+
.serial_number("TEST")])
55+
.unwrap()
56+
.device_class(USB_CLASS_CDC)
57+
.build();
58+
59+
info!("Init done");
60+
61+
loop {
62+
if !usb_dev.poll(&mut [&mut serial]) {
63+
continue;
64+
}
65+
66+
let mut buf = [0u8; 64];
67+
68+
match serial.read(&mut buf) {
69+
Ok(count) if count > 0 => {
70+
led.set_high();
71+
72+
if let Ok(s) = str::from_utf8(&buf[0..count]) {
73+
info!("{:?}", s);
74+
} else {
75+
info!("{:?}", &buf[0..count]);
76+
}
77+
78+
// Echo back in upper case
79+
buf[0..count]
80+
.iter_mut()
81+
.for_each(|c| *c = c.to_ascii_uppercase());
82+
83+
let mut write_offset = 0;
84+
while write_offset < count {
85+
match serial.write(&buf[write_offset..count]) {
86+
Ok(len) if len > 0 => {
87+
write_offset += len;
88+
}
89+
_ => {}
90+
}
91+
}
92+
}
93+
_ => {}
94+
}
95+
96+
led.set_low();
97+
}
98+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub mod spi;
7979
#[cfg(feature = "device-selected")]
8080
pub mod dwt;
8181

82+
#[cfg(feature = "device-selected")]
83+
pub mod usb;
84+
8285
#[cfg(feature = "device-selected")]
8386
mod sealed {
8487
pub trait Sealed {}

src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub use crate::icache::ICacheExt as _stm32h5xx_hal_icache_ICacheExt;
88
pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt;
99
pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt;
1010
pub use crate::spi::SpiExt as _stm32h5xx_hal_spi_SpiExt;
11+
pub use crate::usb::UsbExt as _stm32h5xx_hal_usb_UsbExt;
1112

1213
pub use crate::time::U32Ext as _;
1314
pub use fugit::{ExtU32 as _, RateExtU32 as _};

src/usb.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! USB peripheral.
2+
//!
3+
//! Provides the required implementation for use of the [`stm32-usbd`] crate.
4+
5+
use crate::stm32::rcc::ccipr4::USBSEL;
6+
pub use stm32_usbd::UsbBus;
7+
8+
use crate::gpio;
9+
use crate::gpio::gpioa::{PA11, PA12};
10+
use crate::rcc::{rec, ResetEnable};
11+
use crate::stm32::{self, USB};
12+
use core::marker::PhantomData;
13+
use stm32_usbd::UsbPeripheral;
14+
15+
/// Type for pin that can be the "D-" pin for the USB peripheral
16+
pub type DmPin = PA11<gpio::Alternate<10>>;
17+
18+
/// Type for pin that can be the "D+" pin for the USB peripheral
19+
pub type DpPin = PA12<gpio::Alternate<10>>;
20+
21+
pub trait UsbExt {
22+
fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> UsbDevice;
23+
}
24+
25+
impl UsbExt for stm32::USB {
26+
fn usb(self, rec: rec::Usb, _pin_dm: DmPin, _pin_dp: DpPin) -> UsbDevice {
27+
if let USBSEL::Disable = rec.get_kernel_clk_mux() {
28+
rec.kernel_clk_mux(USBSEL::Hsi48);
29+
};
30+
31+
UsbDevice { _usb: self }
32+
}
33+
}
34+
35+
#[derive(Debug)]
36+
pub struct UsbDevice {
37+
/// USB register block
38+
_usb: USB,
39+
}
40+
41+
#[cfg(feature = "defmt")]
42+
impl defmt::Format for UsbDevice {
43+
fn format(&self, f: defmt::Formatter) {
44+
defmt::write!(f, "UsbDevice {{ usb: USB }}");
45+
}
46+
}
47+
48+
// SAFETY: Implementation of UsbDevice is thread-safe by using cricitcal sections to ensure
49+
// mutually exclusive access to the USB peripheral
50+
unsafe impl Sync for UsbDevice {}
51+
52+
// SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS
53+
unsafe impl UsbPeripheral for UsbDevice {
54+
const REGISTERS: *const () = USB::ptr().cast::<()>();
55+
const DP_PULL_UP_FEATURE: bool = true;
56+
const EP_MEMORY: *const () = 0x4001_6400 as _;
57+
const EP_MEMORY_SIZE: usize = 2048;
58+
const EP_MEMORY_ACCESS: stm32_usbd::MemoryAccess =
59+
stm32_usbd::MemoryAccess::Word32x1;
60+
61+
fn enable() {
62+
cortex_m::interrupt::free(|_| {
63+
#[cfg(any(feature = "h523_h533", feature = "h56x_h573"))]
64+
{
65+
// Safety: we are only touching the usbscr which
66+
// is specific for this peripheral. This together with
67+
// the critical section unsures exclusive access
68+
let pwr = unsafe { &*stm32::PWR::ptr() };
69+
70+
// Enable USB supply level detector
71+
pwr.usbscr().modify(|_, w| w.usb33den().set_bit());
72+
73+
// Await good usb supply voltage
74+
while pwr.vmsr().read().usb33rdy().bit_is_clear() {}
75+
76+
// Set bit to confirm that USB supply level is good
77+
pwr.usbscr().modify(|_, w| w.usb33sv().set_bit());
78+
}
79+
80+
// Reset and enable USB peripheral
81+
rec::Usb {
82+
_marker: PhantomData,
83+
}
84+
.reset()
85+
.enable();
86+
});
87+
}
88+
89+
fn startup_delay() {
90+
// There is a chip specific startup delay. For STM32H503,523,533,56x and 573 it's
91+
// 1µs and this should wait for at least that long.
92+
// 250 Mhz is the highest frequency, so this ensures a minimum of 1µs wait time.
93+
cortex_m::asm::delay(250);
94+
}
95+
}

0 commit comments

Comments
 (0)