Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB peripheral support #135

Merged
merged 9 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
Loading