Skip to content

Commit 1b44464

Browse files
committed
Add usb support and usb_serial example
This was tested on an STM32G473 on a custom board with an external 8MHz oscillator
1 parent aecab39 commit 1b44464

File tree

4 files changed

+188
-0
lines changed

4 files changed

+188
-0
lines changed

examples/usb_serial.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//! CDC-ACM serial port example using polling in a busy loop.
2+
//! This example currently requires an 8MHz external oscillator
3+
//! and assumed an LED is connected to port A6.
4+
//!
5+
//! Further work could be done to setup the HSI48 and the clock
6+
//! recovery system to generate the USB clock.
7+
8+
#![no_std]
9+
#![no_main]
10+
11+
use defmt_rtt as _;
12+
13+
use hal::rcc::PllMDiv;
14+
use hal::rcc::PllNMul;
15+
use hal::rcc::PllQDiv;
16+
use hal::rcc::PllRDiv;
17+
use panic_probe as _;
18+
19+
use stm32g4 as _;
20+
21+
#[cfg(feature = "defmt-logging")]
22+
#[defmt::panic_handler]
23+
fn panic() -> ! {
24+
cortex_m::asm::udf()
25+
}
26+
27+
pub fn exit() -> ! {
28+
loop {
29+
cortex_m::asm::bkpt();
30+
}
31+
}
32+
33+
use hal::rcc::{Config, PLLSrc, Prescaler};
34+
35+
use stm32g4xx_hal as hal;
36+
37+
use hal::prelude::*;
38+
use hal::stm32;
39+
use hal::usb::{Peripheral, UsbBus};
40+
41+
use usb_device::prelude::*;
42+
use usbd_serial::{SerialPort, USB_CLASS_CDC};
43+
44+
#[cortex_m_rt::entry]
45+
fn main() -> ! {
46+
// utils::logger::init();
47+
48+
let dp = stm32::Peripherals::take().unwrap();
49+
50+
let rcc = dp.RCC.constrain();
51+
52+
// This sets the clocks up as follows
53+
// - 8 MHz external oscillator
54+
// - Sysclck and HCLK at 144 MHz
55+
// - APB1 = PCLK1 = 72 MHz
56+
// - APB2 = PCLK2 = 72 MHz
57+
// - USB = 48 MHz
58+
let mut rcc = rcc.freeze(
59+
Config::new(hal::rcc::SysClockSrc::HSE(8.mhz()))
60+
.pll_cfg(hal::rcc::PllConfig {
61+
mux: PLLSrc::HSE(8.mhz()),
62+
m: PllMDiv::DIV_1,
63+
n: PllNMul::MUL_36,
64+
r: Some(PllRDiv::DIV_2),
65+
q: Some(PllQDiv::DIV_6),
66+
p: None,
67+
})
68+
.ahb_psc(Prescaler::Div2)
69+
.apb_psc(Prescaler::Div2),
70+
);
71+
72+
{
73+
use crate::stm32::RCC;
74+
let rcc = unsafe { &*RCC::ptr() };
75+
// Set clock source for USB to PLL
76+
rcc.ccipr.modify(|_, w| w.clk48sel().pllq());
77+
}
78+
79+
// Configure an LED
80+
let gpioa = dp.GPIOA.split(&mut rcc);
81+
82+
let mut led = gpioa.pa6.into_push_pull_output();
83+
84+
let usb = Peripheral { usb: dp.USB };
85+
let usb_bus = UsbBus::new(usb);
86+
87+
let rx_buffer: [u8; 128] = [0; 128];
88+
let tx_buffer: [u8; 128] = [0; 128];
89+
90+
let mut serial = SerialPort::new_with_store(&usb_bus, rx_buffer, tx_buffer);
91+
92+
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
93+
.manufacturer("Fake company")
94+
.product("Serial port")
95+
.serial_number("TEST")
96+
.device_class(USB_CLASS_CDC)
97+
.build();
98+
99+
loop {
100+
if !usb_dev.poll(&mut [&mut serial]) {
101+
continue;
102+
}
103+
104+
let mut buf = [0u8; 64];
105+
106+
match serial.read(&mut buf) {
107+
Ok(count) if count > 0 => {
108+
led.set_low().ok(); // Turn on
109+
110+
// Echo back in upper case
111+
for c in buf[0..count].iter_mut() {
112+
if 0x61 <= *c && *c <= 0x7a {
113+
*c &= !0x20;
114+
}
115+
}
116+
117+
let mut write_offset = 0;
118+
while write_offset < count {
119+
match serial.write(&buf[write_offset..count]) {
120+
Ok(len) if len > 0 => {
121+
write_offset += len;
122+
}
123+
_ => {}
124+
}
125+
}
126+
}
127+
_ => {}
128+
}
129+
130+
led.set_high().ok(); // Turn off
131+
}
132+
}

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,11 @@ pub mod syscfg;
8686
pub mod time;
8787
pub mod timer;
8888
// pub mod watchdog;
89+
90+
#[cfg(all(
91+
feature = "stm32-usbd",
92+
any(
93+
feature = "stm32g473",
94+
)
95+
))]
96+
pub mod usb;

src/time.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct Instant(pub u32);
88
pub struct Bps(pub u32);
99

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

1314
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]

src/usb.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! # USB peripheral.
2+
//!
3+
//! Mostly builds upon the [`stm32_usbd`] crate.
4+
//!
5+
//! ## Examples
6+
//!
7+
//! See [examples/usb_serial.rs] for a usage example.
8+
9+
use crate::stm32::{RCC, USB};
10+
11+
use stm32_usbd::UsbPeripheral;
12+
13+
pub use stm32_usbd::UsbBus;
14+
15+
pub struct Peripheral {
16+
pub usb: USB,
17+
}
18+
19+
unsafe impl Sync for Peripheral {}
20+
21+
unsafe impl UsbPeripheral for Peripheral {
22+
const REGISTERS: *const () = USB::ptr() as *const ();
23+
const DP_PULL_UP_FEATURE: bool = true;
24+
const EP_MEMORY: *const () = 0x4000_6000 as _;
25+
const EP_MEMORY_SIZE: usize = 1024;
26+
const EP_MEMORY_ACCESS_2X16: bool = true;
27+
28+
fn enable() {
29+
let rcc = unsafe { &*RCC::ptr() };
30+
31+
cortex_m::interrupt::free(|_| {
32+
// Enable USB peripheral
33+
rcc.apb1enr1.modify(|_, w| w.usben().enabled());
34+
35+
// Reset USB peripheral
36+
rcc.apb1rstr1.modify(|_, w| w.usbrst().reset());
37+
rcc.apb1rstr1.modify(|_, w| w.usbrst().clear_bit());
38+
});
39+
}
40+
41+
fn startup_delay() {
42+
// 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
43+
cortex_m::asm::delay(170);
44+
}
45+
}
46+
47+
pub type UsbBusType = UsbBus<Peripheral>;

0 commit comments

Comments
 (0)