Skip to content

Commit

Permalink
Add usb support and usb_serial example
Browse files Browse the repository at this point in the history
This was tested on an STM32G473 on a custom board with an external 8MHz oscillator
  • Loading branch information
kevswims committed Apr 18, 2022
1 parent c8e0f59 commit 6a8fabe
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
132 changes: 132 additions & 0 deletions examples/usb_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! CDC-ACM serial port example using polling in a busy loop.
//! This example currently requires an 8MHz external oscillator
//! and assumed an LED is connected to port A6.
//!
//! Further work could be done to setup the HSI48 and the clock
//! recovery system to generate the USB clock.

#![no_std]
#![no_main]

use defmt_rtt as _;

use hal::rcc::PllMDiv;
use hal::rcc::PllNMul;
use hal::rcc::PllQDiv;
use hal::rcc::PllRDiv;
use panic_probe as _;

use stm32g4 as _;

#[cfg(feature = "defmt-logging")]
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}

pub fn exit() -> ! {
loop {
cortex_m::asm::bkpt();
}
}

use hal::rcc::{Config, PLLSrc, Prescaler};

use stm32g4xx_hal as hal;

use hal::prelude::*;
use hal::stm32;
use hal::usb::{Peripheral, UsbBus};

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

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

let dp = stm32::Peripherals::take().unwrap();

let rcc = dp.RCC.constrain();

// This sets the clocks up as follows
// - 8 MHz external oscillator
// - Sysclck and HCLK at 144 MHz
// - APB1 = PCLK1 = 72 MHz
// - APB2 = PCLK2 = 72 MHz
// - USB = 48 MHz
let mut rcc = rcc.freeze(
Config::new(hal::rcc::SysClockSrc::HSE(8.mhz()))
.pll_cfg(hal::rcc::PllConfig {
mux: PLLSrc::HSE(8.mhz()),
m: PllMDiv::DIV_1,
n: PllNMul::MUL_36,
r: Some(PllRDiv::DIV_2),
q: Some(PllQDiv::DIV_6),
p: None,
})
.ahb_psc(Prescaler::Div2)
.apb_psc(Prescaler::Div2),
);

{
use crate::stm32::RCC;
let rcc = unsafe { &*RCC::ptr() };
// Set clock source for USB to PLL
rcc.ccipr.modify(|_, w| w.clk48sel().pllq());
}

// Configure an LED
let gpioa = dp.GPIOA.split(&mut rcc);

let mut led = gpioa.pa6.into_push_pull_output();

let usb = Peripheral { usb: dp.USB };
let usb_bus = UsbBus::new(usb);

let rx_buffer: [u8; 128] = [0; 128];
let tx_buffer: [u8; 128] = [0; 128];

let mut serial = SerialPort::new_with_store(&usb_bus, rx_buffer, tx_buffer);

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Fake company")
.product("Serial port")
.serial_number("TEST")
.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_low().ok(); // Turn on

// 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_high().ok(); // Turn off
}
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,11 @@ pub mod syscfg;
pub mod time;
pub mod timer;
// pub mod watchdog;

#[cfg(all(
feature = "stm32-usbd",
any(
feature = "stm32g473",
)
))]
pub mod usb;
1 change: 1 addition & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct Instant(pub u32);
pub struct Bps(pub u32);

#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Hertz(pub u32);

#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
Expand Down
47 changes: 47 additions & 0 deletions src/usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! # USB peripheral.
//!
//! Mostly builds upon the [`stm32_usbd`] crate.
//!
//! ## Examples
//!
//! See [examples/usb_serial.rs] for a usage example.

use crate::stm32::{RCC, USB};

use stm32_usbd::UsbPeripheral;

pub use stm32_usbd::UsbBus;

pub struct Peripheral {
pub usb: USB,
}

unsafe impl Sync for Peripheral {}

unsafe impl UsbPeripheral for Peripheral {
const REGISTERS: *const () = USB::ptr() as *const ();
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() {
let rcc = unsafe { &*RCC::ptr() };

cortex_m::interrupt::free(|_| {
// Enable USB peripheral
rcc.apb1enr1.modify(|_, w| w.usben().enabled());

// Reset USB peripheral
rcc.apb1rstr1.modify(|_, w| w.usbrst().reset());
rcc.apb1rstr1.modify(|_, w| w.usbrst().clear_bit());
});
}

fn startup_delay() {
// There is a chip specific startup delay. It is not specified for the STM32G4 but the STM32F103 is 1 us to delay for 170 cycles minimum
cortex_m::asm::delay(170);
}
}

pub type UsbBusType = UsbBus<Peripheral>;

0 comments on commit 6a8fabe

Please sign in to comment.