Skip to content

Commit 69a9c70

Browse files
authored
USB peripheral support (#135)
* impl sealed for GPIO types * add USB dependencies * add enable for HSI48 oscillator * implement usb peripheral * add module * fix warnings * add required features for example * remove need for critical section dependency
1 parent bbbd4f6 commit 69a9c70

File tree

6 files changed

+176
-0
lines changed

6 files changed

+176
-0
lines changed

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ bitflags = "1.2"
1919
vcell = "0.1"
2020
static_assertions = "1.1"
2121
fugit = "0.3.5"
22+
stm32-usbd = { version = "0.7.0", optional = true }
2223
fixed = { version = "1.28.0", optional = true }
2324

2425
[dependencies.cortex-m]
@@ -72,11 +73,14 @@ cfg-if = "0.1.10"
7273
mpu6050 = "0.1.4"
7374
bme680 = "0.6.0"
7475
embedded-sdmmc = "0.3.0"
76+
usb-device = { version = "0.3.2", features = ["defmt"] }
77+
usbd-serial = "0.2.2"
7578

7679
#TODO: Separate feature sets
7780
[features]
7881
default = ["rt"]
7982
rt = ["stm32g4/rt"]
83+
usb = ["dep:stm32-usbd"]
8084
stm32g431 = ["stm32g4/stm32g431"]
8185
stm32g441 = ["stm32g4/stm32g441"]
8286
stm32g471 = ["stm32g4/stm32g471"]
@@ -108,6 +112,10 @@ lto = true
108112
name = "flash_with_rtic"
109113
required-features = ["stm32g474"]
110114

115+
[[example]]
116+
name = "usb_serial"
117+
required-features = ["usb"]
118+
111119
[[example]]
112120
name = "cordic"
113121
required-features = ["cordic"]

examples/usb_serial.rs

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

src/gpio.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ macro_rules! gpio {
342342
_mode: PhantomData<MODE>,
343343
}
344344

345+
impl<MODE> crate::Sealed for $PXi<MODE> {}
346+
345347
#[allow(clippy::from_over_into)]
346348
impl Into<$PXi<Input<PullDown>>> for $PXi<DefaultMode> {
347349
fn into(self) -> $PXi<Input<PullDown>> {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,5 @@ pub mod time;
100100
pub mod timer;
101101
// pub mod watchdog;
102102
pub mod independent_watchdog;
103+
#[cfg(feature = "usb")]
104+
pub mod usb;

src/rcc/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ impl Rcc {
440440
while self.rb.csr.read().lsirdy().bit_is_clear() {}
441441
}
442442

443+
pub fn enable_hsi48(&self) {
444+
self.rb.crrcr.modify(|_, w| w.hsi48on().set_bit());
445+
while self.rb.crrcr.read().hsi48rdy().bit_is_clear() {}
446+
}
447+
443448
pub fn get_reset_reason(&self) -> ResetReason {
444449
let csr = self.rb.csr.read();
445450

src/usb.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! USB peripheral.
2+
//!
3+
//! Provides the required implementation for use of the [`stm32-usbd`] crate.
4+
5+
pub use stm32_usbd::UsbBus;
6+
7+
use crate::gpio;
8+
use crate::gpio::gpioa::{PA11, PA12};
9+
use crate::rcc::{Enable, Reset};
10+
use crate::stm32::{RCC, USB};
11+
use core::fmt;
12+
use stm32_usbd::UsbPeripheral;
13+
14+
/// Trait implemented by all pins that can be the "D-" pin for the USB peripheral
15+
pub trait DmPin: crate::Sealed {}
16+
17+
/// Trait implemented by all pins that can be the "D+" pin for the USB peripheral
18+
pub trait DpPin: crate::Sealed {}
19+
20+
impl DmPin for PA11<gpio::Alternate<{ gpio::AF14 }>> {}
21+
impl DpPin for PA12<gpio::Alternate<{ gpio::AF14 }>> {}
22+
23+
pub struct Peripheral<Dm: DmPin, Dp: DpPin> {
24+
/// USB register block
25+
pub usb: USB,
26+
/// Data negative pin
27+
pub pin_dm: Dm,
28+
/// Data positive pin
29+
pub pin_dp: Dp,
30+
}
31+
32+
impl<Dm, Dp> fmt::Debug for Peripheral<Dm, Dp>
33+
where
34+
Dm: DmPin + fmt::Debug,
35+
Dp: DpPin + fmt::Debug,
36+
{
37+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38+
f.debug_struct("Peripheral")
39+
.field("usb", &"USB")
40+
.field("pin_dm", &self.pin_dm)
41+
.field("pin_dp", &self.pin_dp)
42+
.finish()
43+
}
44+
}
45+
46+
// SAFETY: Implementation of Peripheral is thread-safe by using cricitcal sections to ensure
47+
// mutually exclusive access to the USB peripheral
48+
unsafe impl<Dm: DmPin, Dp: DpPin> Sync for Peripheral<Dm, Dp> {}
49+
50+
// SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS
51+
unsafe impl<Dm: DmPin + Send, Dp: DpPin + Send> UsbPeripheral for Peripheral<Dm, Dp> {
52+
const REGISTERS: *const () = USB::ptr().cast::<()>();
53+
const DP_PULL_UP_FEATURE: bool = true;
54+
const EP_MEMORY: *const () = 0x4000_6000 as _;
55+
const EP_MEMORY_SIZE: usize = 1024;
56+
const EP_MEMORY_ACCESS_2X16: bool = true;
57+
58+
fn enable() {
59+
cortex_m::interrupt::free(|_| unsafe {
60+
let rcc_ptr = &(*RCC::ptr());
61+
USB::enable(rcc_ptr);
62+
USB::reset(rcc_ptr);
63+
});
64+
}
65+
66+
fn startup_delay() {
67+
// not required
68+
}
69+
}

0 commit comments

Comments
 (0)