Skip to content

Commit ff2fcd8

Browse files
authored
Merge pull request #216 from engstad/stm32l4x6-otg
Experimental support for USB OTG FS for L4x5 and L4x6 lines.
2 parents 35946d6 + f8edcf4 commit ff2fcd8

File tree

4 files changed

+266
-0
lines changed

4 files changed

+266
-0
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ version = "0.5.0"
5050
features = ["ram_access_2x16"]
5151
optional = true
5252

53+
[dependencies.synopsys-usb-otg]
54+
version = "0.2.4"
55+
features = ["cortex-m", "fs"]
56+
optional = true
57+
5358
[package.metadata.docs.rs]
5459
features = ["rt", "stm32l4x2", "stm32-usbd"]
5560

@@ -61,6 +66,7 @@ stm32l4x3 = ["stm32l4/stm32l4x3"]
6166
stm32l4x5 = ["stm32l4/stm32l4x5"]
6267
stm32l4x6 = ["stm32l4/stm32l4x6"]
6368
unproven = ["embedded-hal/unproven"]
69+
otg_fs = ["synopsys-usb-otg"]
6470

6571
[dev-dependencies]
6672
panic-halt = "0.2.0"
@@ -126,6 +132,11 @@ required-features = ["rt"]
126132
name = "usb_serial"
127133
required-features = ["rt", "stm32l4x2", "stm32-usbd"]
128134

135+
[[example]]
136+
name = "otg_fs_serial"
137+
required-features = ["rt", "stm32l4x6", "otg_fs"]
138+
129139
[[example]]
130140
name = "i2c_write"
131141
required-features = ["stm32l4x1"]
142+

examples/otg_fs_serial.rs

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

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ pub mod gpio;
117117
feature = "stm32l4x6"
118118
))]
119119
pub mod i2c;
120+
#[cfg(all(feature = "otg_fs", any(feature = "stm32l4x5", feature = "stm32l4x6")))]
121+
pub mod otg_fs;
120122
#[cfg(any(
121123
feature = "stm32l4x1",
122124
feature = "stm32l4x2",

src/otg_fs.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! USB OTG full-speed peripheral
2+
//!
3+
//! The STM32L4 series only supports the full-speed peripheral.
4+
5+
use crate::stm32;
6+
7+
use crate::gpio::{
8+
gpioa::{PA11, PA12},
9+
Alternate, Floating, Input, AF10,
10+
};
11+
use crate::time::Hertz;
12+
13+
pub use synopsys_usb_otg::UsbBus;
14+
use synopsys_usb_otg::UsbPeripheral;
15+
16+
pub struct USB {
17+
pub usb_global: stm32::OTG_FS_GLOBAL,
18+
pub usb_device: stm32::OTG_FS_DEVICE,
19+
pub usb_pwrclk: stm32::OTG_FS_PWRCLK,
20+
pub pin_dm: PA11<Alternate<AF10, Input<Floating>>>,
21+
pub pin_dp: PA12<Alternate<AF10, Input<Floating>>>,
22+
pub hclk: Hertz,
23+
}
24+
25+
unsafe impl Sync for USB {}
26+
27+
unsafe impl UsbPeripheral for USB {
28+
const REGISTERS: *const () = stm32::OTG_FS_GLOBAL::ptr() as *const ();
29+
30+
const HIGH_SPEED: bool = false;
31+
const FIFO_DEPTH_WORDS: usize = 320;
32+
33+
const ENDPOINT_COUNT: usize = 6;
34+
35+
fn enable() {
36+
let rcc = unsafe { &*stm32::RCC::ptr() };
37+
38+
cortex_m::interrupt::free(|_| {
39+
// Enable USB peripheral
40+
rcc.ahb2enr.modify(|_, w| w.otgfsen().set_bit());
41+
let _ = rcc.ahb2enr.read().otgfsen().bit_is_set();
42+
43+
// Reset USB peripheral
44+
rcc.ahb2rstr.modify(|_, w| w.otgfsrst().set_bit());
45+
rcc.ahb2rstr.modify(|_, w| w.otgfsrst().clear_bit());
46+
});
47+
}
48+
49+
fn ahb_frequency_hz(&self) -> u32 {
50+
self.hclk.0
51+
}
52+
}
53+
54+
pub type UsbBusType = UsbBus<USB>;

0 commit comments

Comments
 (0)