Skip to content

Commit

Permalink
USB peripheral support (#135)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
liamkinne authored Oct 31, 2024
1 parent bbbd4f6 commit 69a9c70
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bitflags = "1.2"
vcell = "0.1"
static_assertions = "1.1"
fugit = "0.3.5"
stm32-usbd = { version = "0.7.0", optional = true }
fixed = { version = "1.28.0", optional = true }

[dependencies.cortex-m]
Expand Down Expand Up @@ -72,11 +73,14 @@ cfg-if = "0.1.10"
mpu6050 = "0.1.4"
bme680 = "0.6.0"
embedded-sdmmc = "0.3.0"
usb-device = { version = "0.3.2", features = ["defmt"] }
usbd-serial = "0.2.2"

#TODO: Separate feature sets
[features]
default = ["rt"]
rt = ["stm32g4/rt"]
usb = ["dep:stm32-usbd"]
stm32g431 = ["stm32g4/stm32g431"]
stm32g441 = ["stm32g4/stm32g441"]
stm32g471 = ["stm32g4/stm32g471"]
Expand Down Expand Up @@ -108,6 +112,10 @@ lto = true
name = "flash_with_rtic"
required-features = ["stm32g474"]

[[example]]
name = "usb_serial"
required-features = ["usb"]

[[example]]
name = "cordic"
required-features = ["cordic"]
90 changes: 90 additions & 0 deletions examples/usb_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! CDC-ACM serial port example using polling in a busy loop.
#![deny(warnings)]
#![deny(unsafe_code)]
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use hal::prelude::*;
use hal::pwr::PwrExt;
use hal::usb::{Peripheral, UsbBus};
use hal::{rcc, stm32};
use stm32g4xx_hal as hal;

use usb_device::prelude::*;
use usbd_serial::{SerialPort, USB_CLASS_CDC};

use panic_probe as _;

#[macro_use]
mod utils;

#[entry]
fn main() -> ! {
utils::logger::init();

let dp = stm32::Peripherals::take().expect("cannot take peripherals");
let pwr = dp.PWR.constrain().freeze();
let mut rcc = dp.RCC.freeze(rcc::Config::hsi(), pwr);
rcc.enable_hsi48();

let gpioa = dp.GPIOA.split(&mut rcc);

let mut led = gpioa.pa5.into_push_pull_output();
led.set_low().ok();

let usb_dm = gpioa.pa11.into_alternate();
let usb_dp = gpioa.pa12.into_alternate();

let usb = Peripheral {
usb: dp.USB,
pin_dm: usb_dm,
pin_dp: 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();

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().ok();

// 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().ok();
}
}
2 changes: 2 additions & 0 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ macro_rules! gpio {
_mode: PhantomData<MODE>,
}

impl<MODE> crate::Sealed for $PXi<MODE> {}

#[allow(clippy::from_over_into)]
impl Into<$PXi<Input<PullDown>>> for $PXi<DefaultMode> {
fn into(self) -> $PXi<Input<PullDown>> {
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,5 @@ pub mod time;
pub mod timer;
// pub mod watchdog;
pub mod independent_watchdog;
#[cfg(feature = "usb")]
pub mod usb;
5 changes: 5 additions & 0 deletions src/rcc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ impl Rcc {
while self.rb.csr.read().lsirdy().bit_is_clear() {}
}

pub fn enable_hsi48(&self) {
self.rb.crrcr.modify(|_, w| w.hsi48on().set_bit());
while self.rb.crrcr.read().hsi48rdy().bit_is_clear() {}
}

pub fn get_reset_reason(&self) -> ResetReason {
let csr = self.rb.csr.read();

Expand Down
69 changes: 69 additions & 0 deletions src/usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! USB peripheral.
//!
//! Provides the required implementation for use of the [`stm32-usbd`] crate.
pub use stm32_usbd::UsbBus;

use crate::gpio;
use crate::gpio::gpioa::{PA11, PA12};
use crate::rcc::{Enable, Reset};
use crate::stm32::{RCC, USB};
use core::fmt;
use stm32_usbd::UsbPeripheral;

/// Trait implemented by all pins that can be the "D-" pin for the USB peripheral
pub trait DmPin: crate::Sealed {}

/// Trait implemented by all pins that can be the "D+" pin for the USB peripheral
pub trait DpPin: crate::Sealed {}

impl DmPin for PA11<gpio::Alternate<{ gpio::AF14 }>> {}
impl DpPin for PA12<gpio::Alternate<{ gpio::AF14 }>> {}

pub struct Peripheral<Dm: DmPin, Dp: DpPin> {
/// USB register block
pub usb: USB,
/// Data negative pin
pub pin_dm: Dm,
/// Data positive pin
pub pin_dp: Dp,
}

impl<Dm, Dp> fmt::Debug for Peripheral<Dm, Dp>
where
Dm: DmPin + fmt::Debug,
Dp: DpPin + fmt::Debug,
{
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<Dm: DmPin, Dp: DpPin> Sync for Peripheral<Dm, Dp> {}

// SAFETY: The peripheral has the same regiter blockout as the STM32 USBFS
unsafe impl<Dm: DmPin + Send, Dp: DpPin + Send> UsbPeripheral for Peripheral<Dm, Dp> {
const REGISTERS: *const () = USB::ptr().cast::<()>();
const DP_PULL_UP_FEATURE: bool = true;
const EP_MEMORY: *const () = 0x4000_6000 as _;
const EP_MEMORY_SIZE: usize = 1024;
const EP_MEMORY_ACCESS_2X16: bool = true;

fn enable() {
cortex_m::interrupt::free(|_| unsafe {
let rcc_ptr = &(*RCC::ptr());
USB::enable(rcc_ptr);
USB::reset(rcc_ptr);
});
}

fn startup_delay() {
// not required
}
}

0 comments on commit 69a9c70

Please sign in to comment.