Skip to content

Commit b6c7432

Browse files
committed
Implement USB support, inspiration taken from G4, F3, F4 and H7 hals
1 parent d3e08ba commit b6c7432

File tree

5 files changed

+223
-0
lines changed

5 files changed

+223
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ embedded-hal = "1.0.0"
7070
defmt = { version = "1.0.0", optional = true }
7171
paste = "1.0.15"
7272
log = { version = "0.4.20", optional = true}
73+
stm32-usbd = "0.8.0"
7374

7475
[dev-dependencies]
7576
log = { version = "0.4.20"}
@@ -85,6 +86,8 @@ cortex-m-semihosting = "0.5.0"
8586
panic-itm = { version = "~0.4.1" }
8687
panic-probe = "0.3.2"
8788
panic-semihosting = "0.6"
89+
usbd-serial = "0.2.2"
90+
usb-device = { version = "0.3.2", features = ["defmt", "log"] }
8891

8992
[profile.release]
9093
codegen-units = 1 # better optimizations

examples/usb_serial.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
for c in buf[0..count].iter_mut() {
80+
if 0x61 <= *c && *c <= 0x7a {
81+
*c &= !0x20;
82+
}
83+
}
84+
85+
let mut write_offset = 0;
86+
while write_offset < count {
87+
match serial.write(&buf[write_offset..count]) {
88+
Ok(len) if len > 0 => {
89+
write_offset += len;
90+
}
91+
_ => {}
92+
}
93+
}
94+
}
95+
_ => {}
96+
}
97+
98+
led.set_low();
99+
}
100+
}

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: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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;
11+
use crate::stm32::{self, USB};
12+
use core::fmt;
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) -> Peripheral;
23+
}
24+
25+
impl UsbExt for stm32::USB {
26+
fn usb(self, rec: rec::Usb, pin_dm: DmPin, pin_dp: DpPin) -> Peripheral {
27+
if let USBSEL::Disable = rec.get_kernel_clk_mux() {
28+
rec.kernel_clk_mux(USBSEL::Hsi48);
29+
};
30+
31+
Peripheral {
32+
_usb: self,
33+
pin_dm,
34+
pin_dp,
35+
}
36+
}
37+
}
38+
39+
pub struct Peripheral {
40+
/// USB register block
41+
_usb: USB,
42+
/// Data negative pin
43+
pin_dm: DmPin,
44+
/// Data positive pin
45+
pin_dp: DpPin,
46+
}
47+
48+
#[cfg(feature = "defmt")]
49+
impl defmt::Format for Peripheral {
50+
fn format(&self, f: defmt::Formatter) {
51+
defmt::write!(
52+
f,
53+
"Peripheral {{ usb: USB, pin_dm: {}, pin_dp: {}}}",
54+
self.pin_dm,
55+
self.pin_dp
56+
);
57+
}
58+
}
59+
60+
impl fmt::Debug for Peripheral {
61+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62+
f.debug_struct("Peripheral")
63+
.field("usb", &"USB")
64+
.field("pin_dm", &self.pin_dm)
65+
.field("pin_dp", &self.pin_dp)
66+
.finish()
67+
}
68+
}
69+
70+
// SAFETY: Implementation of Peripheral is thread-safe by using cricitcal sections to ensure
71+
// mutually exclusive access to the USB peripheral
72+
unsafe impl Sync for Peripheral {}
73+
74+
// SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS
75+
unsafe impl UsbPeripheral for Peripheral {
76+
const REGISTERS: *const () = USB::ptr().cast::<()>();
77+
const DP_PULL_UP_FEATURE: bool = true;
78+
const EP_MEMORY: *const () = 0x4001_6400 as _;
79+
const EP_MEMORY_SIZE: usize = 2048;
80+
const EP_MEMORY_ACCESS: stm32_usbd::MemoryAccess =
81+
stm32_usbd::MemoryAccess::Word32x1;
82+
83+
fn enable() {
84+
cortex_m::interrupt::free(|_| {
85+
let rcc = unsafe { &*stm32::RCC::ptr() };
86+
87+
#[cfg(any(feature = "h523_h533", feature = "h56x_h573"))]
88+
{
89+
let pwr = unsafe { &*stm32::PWR::ptr() };
90+
91+
// Enable USB supply level detector
92+
pwr.usbscr().modify(|_, w| w.usb33den().set_bit());
93+
94+
// Await good usb supply voltage
95+
while pwr.vmsr().read().usb33rdy().bit_is_clear() {}
96+
97+
// Set bit to confirm that USB supply level is good
98+
pwr.usbscr().modify(|_, w| w.usb33sv().set_bit());
99+
}
100+
101+
// Enable USB peripheral
102+
rcc.apb2enr().modify(|_, w| w.usben().set_bit());
103+
104+
// Reset USB peripheral
105+
rcc.apb2rstr().modify(|_, w| w.usbrst().set_bit());
106+
rcc.apb2rstr().modify(|_, w| w.usbrst().clear_bit());
107+
});
108+
}
109+
110+
fn startup_delay() {
111+
// There is a chip specific startup delay. For STM32H503,523,533,56x and 573 it's
112+
// 1µs and this should wait for at least that long.
113+
// 250 Mhz is the highest frequency, so this ensures a minimum of 1µs wait time.
114+
cortex_m::asm::delay(250);
115+
}
116+
}

0 commit comments

Comments
 (0)