Skip to content

Commit 5075c02

Browse files
committed
Add sample for USB OTG FS.
1 parent 1bb0773 commit 5075c02

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

examples/otg_fs_serial.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
//! OTG USB 2.0 FS serial port example using polling in a busy loop.
2+
//!
3+
//! Note: Must build with features "stm32l4x5 otg_fs" or "stm32l4x6 otg_fs".
4+
#![no_main]
5+
#![no_std]
6+
7+
extern crate panic_semihosting;
8+
9+
use cortex_m_rt::entry;
10+
use stm32l4xx_hal::stm32::{RCC, CRS, PWR, Peripherals};
11+
use stm32l4xx_hal::rcc::{PllConfig, PllDivider, PllSource, MsiFreq, CrystalBypass, ClockSecuritySystem};
12+
use stm32l4xx_hal::gpio::Speed;
13+
use stm32l4xx_hal::otg_fs::{USB, UsbBus};
14+
use stm32l4xx_hal::prelude::*;
15+
use usb_device::prelude::*;
16+
17+
/// Enable CRS (Clock Recovery System)
18+
fn enable_crs() {
19+
let rcc = unsafe { &(*RCC::ptr()) };
20+
rcc.apb1enr1.modify(|_, w| w.crsen().set_bit());
21+
let crs = unsafe { &(*CRS::ptr()) };
22+
// Initialize clock recovery
23+
// Set autotrim enabled.
24+
crs.cr.modify(|_, w| w.autotrimen().set_bit());
25+
// Enable CR
26+
crs.cr.modify(|_, w| w.cen().set_bit());
27+
}
28+
29+
/// Enables VddUSB power supply
30+
fn enable_usb_pwr() {
31+
// Enable PWR peripheral
32+
let rcc = unsafe { &(*RCC::ptr()) };
33+
rcc.apb1enr1.modify(|_, w| w.pwren().set_bit());
34+
35+
// Enable VddUSB
36+
let pwr = unsafe { &*PWR::ptr() };
37+
pwr.cr2.modify(|_, w| w.usv().set_bit());
38+
}
39+
40+
/// Reset peripherals to known state.
41+
unsafe fn reset_peripherals(dp: &Peripherals) {
42+
dp.RCC.cr.modify(|_, w| w.msion().set_bit());
43+
dp.RCC.cfgr.modify(|_, w| {
44+
w.sw().bits(0);
45+
w.hpre().bits(0);
46+
w.ppre1().bits(0);
47+
w.ppre2().bits(0);
48+
w.mcosel().bits(0);
49+
w
50+
});
51+
dp.RCC.cr.modify(|_, w| {
52+
w.pllsai2on().clear_bit();
53+
w.pllsai1on().clear_bit();
54+
w.pllon().clear_bit();
55+
w.hsion().clear_bit();
56+
w.csson().clear_bit();
57+
w.hseon().clear_bit();
58+
w
59+
});
60+
dp.RCC.pllcfgr.modify(|_, w| {
61+
w.pllpdiv().bits(0);
62+
w.pllr().bits(0);
63+
w.pllren().clear_bit();
64+
w.pllq().bits(0);
65+
w.pllqen().clear_bit();
66+
w.pllp().clear_bit();
67+
w.pllpen().clear_bit();
68+
w.plln().bits(1 << 4);
69+
w.pllm().bits(0);
70+
w.pllsrc().bits(0);
71+
w
72+
});
73+
74+
dp.RCC.crrcr.modify(|_, w| w.hsi48on().clear_bit());
75+
dp.RCC.cr.modify(|_, w| w.hsebyp().clear_bit());
76+
77+
dp.RCC.pllcfgr.modify(|_, w| {
78+
w.pllsrc().bits(0);
79+
w.pllpdiv().bits(0);
80+
w
81+
});
82+
83+
dp.RCC.cier.reset();
84+
85+
dp.FLASH.acr.modify(|_, w| w.bits(4));
86+
}
87+
88+
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
89+
90+
#[entry]
91+
unsafe fn main() -> ! {
92+
let dp = Peripherals::take().unwrap();
93+
94+
//reset_peripherals(&dp);
95+
96+
let mut flash = dp.FLASH.constrain();
97+
let mut rcc = dp.RCC.constrain();
98+
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
99+
100+
// Set to true if external 16 MHz high-speed resonator/crystal is used.
101+
const USE_HSE_CLK: bool = true;
102+
103+
let clocks = {
104+
if !USE_HSE_CLK {
105+
// 48 MHz / 6 * 40 / 4 = 80 MHz
106+
let pll_cfg = PllConfig::new(6, 40, PllDivider::Div4);
107+
108+
// Note: If program needs low-speed clocks, adjust this.
109+
rcc.cfgr
110+
.msi(MsiFreq::RANGE48M) // Set the MSI (multi-speed internal) clock to 48 MHz
111+
.pll_source(PllSource::MSI)
112+
.sysclk_with_pll(80.mhz(), pll_cfg)
113+
.pclk1(24.mhz())
114+
.pclk2(24.mhz())
115+
.freeze(&mut flash.acr, &mut pwr)
116+
} else {
117+
// Note: If program needs low-speed clocks, adjust this.
118+
// Tested using a 16 MHz resonator.
119+
rcc.cfgr
120+
.msi(MsiFreq::RANGE48M)
121+
.hse(
122+
16.mhz(),
123+
CrystalBypass::Disable, // Bypass enabled when clock signals instead of crystals/resonators are used.
124+
ClockSecuritySystem::Disable, // We have not set up interrupt routines handling clock drifts/errors.
125+
)
126+
.pll_source(PllSource::HSE)
127+
.sysclk(80.mhz())
128+
.freeze(&mut flash.acr, &mut pwr)
129+
}
130+
};
131+
132+
// Enable clock recovery system.
133+
enable_crs();
134+
// Enable USB power (and disable VddUSB power isolation).
135+
enable_usb_pwr();
136+
137+
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
138+
139+
let usb = USB {
140+
usb_global: dp.OTG_FS_GLOBAL,
141+
usb_device: dp.OTG_FS_DEVICE,
142+
usb_pwrclk: dp.OTG_FS_PWRCLK,
143+
hclk: clocks.hclk(),
144+
pin_dm: gpioa
145+
.pa11
146+
.into_af10(&mut gpioa.moder, &mut gpioa.afrh)
147+
.set_speed(Speed::VeryHigh),
148+
pin_dp: gpioa
149+
.pa12
150+
.into_af10(&mut gpioa.moder, &mut gpioa.afrh)
151+
.set_speed(Speed::VeryHigh),
152+
};
153+
154+
let usb_bus = UsbBus::new(usb, &mut EP_MEMORY);
155+
156+
let mut usb_serial = usbd_serial::SerialPort::new(&usb_bus);
157+
158+
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
159+
.manufacturer("Fake Company")
160+
.product("Serial port")
161+
.serial_number("TEST")
162+
.device_class(usbd_serial::USB_CLASS_CDC)
163+
.build();
164+
165+
#[cfg(feature = "semihosting")]
166+
hprintln!("Polling!").ok();
167+
168+
loop {
169+
if !usb_dev.poll(&mut [&mut usb_serial]) {
170+
continue;
171+
}
172+
173+
let mut buf = [0u8; 64];
174+
175+
match usb_serial.read(&mut buf) {
176+
Ok(count) if count > 0 => {
177+
// Echo back in upper case
178+
for c in buf[0..count].iter_mut() {
179+
if 0x61 <= *c && *c <= 0x7a {
180+
*c &= !0x20;
181+
}
182+
}
183+
184+
let mut write_offset = 0;
185+
while write_offset < count {
186+
match usb_serial.write(&buf[write_offset..count]) {
187+
Ok(len) if len > 0 => {
188+
write_offset += len;
189+
}
190+
_ => {}
191+
}
192+
}
193+
}
194+
_ => {}
195+
}
196+
}
197+
}

0 commit comments

Comments
 (0)