Skip to content

Commit 7905ce9

Browse files
bors[bot]richardeoinolback
authored
Merge #300
300: USB example improvements r=richardeoin a=richardeoin - [x] Add notes about internal USB regulator to examples - [x] Add an interrupt-driven example for USB, maybe using RTIC Co-authored-by: Richard Meadows <962920+richardeoin@users.noreply.github.com> Co-authored-by: Edwin Svensson <git@olback.net>
2 parents 7e08ea4 + 3c798a3 commit 7905ce9

File tree

6 files changed

+386
-1
lines changed

6 files changed

+386
-1
lines changed

Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ bare-metal = "1.0.0"
3939
sdio-host = { version = "0.5", optional = true }
4040
embedded-sdmmc = { version = "0.3", optional = true }
4141
stm32-fmc = { version = "0.2", optional = true }
42-
synopsys-usb-otg = { version = "^0.2.4", features = ["cortex-m"], optional = true }
42+
synopsys-usb-otg = { version = "^0.3.0", features = ["cortex-m"], optional = true }
4343
embedded-display-controller = { version = "^0.1.0", optional = true }
4444

4545
[dependencies.smoltcp]
@@ -202,10 +202,18 @@ required-features = ["rt"]
202202
name = "usb_serial"
203203
required-features = ["usb_hs"]
204204

205+
[[example]]
206+
name = "usb_rtic"
207+
required-features = ["rt", "usb_hs", "rm0433"]
208+
205209
[[example]]
206210
name = "usb_passthrough"
207211
required-features = ["usb_hs", "rm0399"]
208212

213+
[[example]]
214+
name = "usb_phy_serial_interrupt"
215+
required-features = ["rt", "usb_hs", "rm0433"]
216+
209217
[[example]]
210218
name = "rtc"
211219
required-features = ["rt", "rtc"]

examples/usb_passthrough.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ fn main() -> ! {
3333
let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run");
3434
ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::HSI48);
3535

36+
// If your hardware uses the internal USB voltage regulator in ON mode, you
37+
// should uncomment this block.
38+
// unsafe {
39+
// let pwr = &*stm32::PWR::ptr();
40+
// pwr.cr3.modify(|_, w| w.usbregen().set_bit());
41+
// while pwr.cr3.read().usb33rdy().bit_is_clear() {}
42+
// }
43+
3644
// IO
3745
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
3846
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);

examples/usb_phy_serial_interrupt.rs

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//! CDC-ACM serial port example using an external phy and interrupts
2+
#![no_std]
3+
#![no_main]
4+
5+
use {
6+
core::cell::RefCell,
7+
cortex_m::interrupt::{free as interrupt_free, Mutex},
8+
stm32h7xx_hal::{
9+
hal::digital::v2::OutputPin,
10+
interrupt, pac,
11+
prelude::*,
12+
rcc::rec::UsbClkSel,
13+
stm32,
14+
usb_hs::{UsbBus, USB1_ULPI},
15+
},
16+
usb_device::prelude::*,
17+
usb_device::{bus::UsbBusAllocator, device::UsbDevice},
18+
usbd_serial::{DefaultBufferStore, SerialPort},
19+
};
20+
21+
#[macro_use]
22+
#[allow(unused)]
23+
mod utilities;
24+
25+
pub const VID: u16 = 0x2341;
26+
pub const PID: u16 = 0x025b;
27+
28+
pub static mut USB_MEMORY_1: [u32; 1024] = [0u32; 1024];
29+
pub static mut USB_BUS_ALLOCATOR: Option<UsbBusAllocator<UsbBus<USB1_ULPI>>> =
30+
None;
31+
pub static SERIAL_PORT: Mutex<
32+
RefCell<
33+
Option<
34+
SerialPort<
35+
UsbBus<USB1_ULPI>,
36+
DefaultBufferStore,
37+
DefaultBufferStore,
38+
>,
39+
>,
40+
>,
41+
> = Mutex::new(RefCell::new(None));
42+
pub static USB_DEVICE: Mutex<RefCell<Option<UsbDevice<UsbBus<USB1_ULPI>>>>> =
43+
Mutex::new(RefCell::new(None));
44+
45+
#[cortex_m_rt::entry]
46+
unsafe fn main() -> ! {
47+
let cp = cortex_m::Peripherals::take().unwrap();
48+
let dp = stm32::Peripherals::take().unwrap();
49+
50+
// Power
51+
let pwr = dp.PWR.constrain();
52+
let vos = example_power!(pwr).freeze();
53+
54+
// RCC
55+
let rcc = dp.RCC.constrain();
56+
let mut ccdr = rcc.sys_ck(80.mhz()).freeze(vos, &dp.SYSCFG);
57+
58+
// 48MHz CLOCK
59+
let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run");
60+
ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::HSI48);
61+
62+
// If your hardware uses the internal USB voltage regulator in ON mode, you
63+
// should uncomment this block.
64+
// unsafe {
65+
// let pwr = &*stm32::PWR::ptr();
66+
// pwr.cr3.modify(|_, w| w.usbregen().set_bit());
67+
// while pwr.cr3.read().usb33rdy().bit_is_clear() {}
68+
// }
69+
70+
// Get the delay provider.
71+
let mut delay = cp.SYST.delay(ccdr.clocks);
72+
73+
// GPIO
74+
let (
75+
gpioa,
76+
gpiob,
77+
gpioc,
78+
_gpiod,
79+
_gpioe,
80+
_gpiof,
81+
_gpiog,
82+
gpioh,
83+
gpioi,
84+
gpioj,
85+
_gpiok,
86+
) = {
87+
(
88+
dp.GPIOA.split(ccdr.peripheral.GPIOA),
89+
dp.GPIOB.split(ccdr.peripheral.GPIOB),
90+
dp.GPIOC.split(ccdr.peripheral.GPIOC),
91+
dp.GPIOD.split(ccdr.peripheral.GPIOD),
92+
dp.GPIOE.split(ccdr.peripheral.GPIOE),
93+
dp.GPIOF.split(ccdr.peripheral.GPIOF),
94+
dp.GPIOG.split(ccdr.peripheral.GPIOG),
95+
dp.GPIOH.split(ccdr.peripheral.GPIOH),
96+
dp.GPIOI.split(ccdr.peripheral.GPIOI),
97+
dp.GPIOJ.split(ccdr.peripheral.GPIOJ),
98+
dp.GPIOK.split(ccdr.peripheral.GPIOK),
99+
)
100+
};
101+
102+
// It is very likely that the board you're using has an external
103+
// clock source. Sometimes you have to enable them by yourself.
104+
// This is the case on the Arduino Portenta H7.
105+
let mut oscen = gpioh.ph1.into_push_pull_output();
106+
delay.delay_ms(10u32);
107+
oscen.set_high().unwrap();
108+
// Wait for osc to be stable
109+
delay.delay_ms(100u32);
110+
111+
// Set USB OTG pin floating
112+
let mut _usb_otg = gpioj.pj6.into_floating_input();
113+
114+
// Reset USB Phy
115+
let mut usb_phy_rst = gpioj.pj4.into_push_pull_output();
116+
usb_phy_rst.set_low().unwrap();
117+
delay.delay_ms(10u8);
118+
usb_phy_rst.set_high().unwrap();
119+
delay.delay_ms(10u8);
120+
121+
// Enable USB OTG_HS interrupt
122+
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OTG_HS);
123+
124+
let usb = USB1_ULPI::new(
125+
dp.OTG1_HS_GLOBAL,
126+
dp.OTG1_HS_DEVICE,
127+
dp.OTG1_HS_PWRCLK,
128+
gpioa.pa5.into_alternate_af10(),
129+
gpioi.pi11.into_alternate_af10(),
130+
gpioh.ph4.into_alternate_af10(),
131+
gpioc.pc0.into_alternate_af10(),
132+
gpioa.pa3.into_alternate_af10(),
133+
gpiob.pb0.into_alternate_af10(),
134+
gpiob.pb1.into_alternate_af10(),
135+
gpiob.pb10.into_alternate_af10(),
136+
gpiob.pb11.into_alternate_af10(),
137+
gpiob.pb12.into_alternate_af10(),
138+
gpiob.pb13.into_alternate_af10(),
139+
gpiob.pb5.into_alternate_af10(),
140+
ccdr.peripheral.USB1OTG,
141+
&ccdr.clocks,
142+
);
143+
144+
USB_BUS_ALLOCATOR = Some(UsbBus::new(usb, &mut USB_MEMORY_1));
145+
146+
let usb_serial =
147+
usbd_serial::SerialPort::new(USB_BUS_ALLOCATOR.as_ref().unwrap());
148+
149+
let usb_dev = UsbDeviceBuilder::new(
150+
USB_BUS_ALLOCATOR.as_ref().unwrap(),
151+
UsbVidPid(VID, PID),
152+
)
153+
.manufacturer("Fake company")
154+
.product("Serial port")
155+
.serial_number("TEST")
156+
.device_class(usbd_serial::USB_CLASS_CDC)
157+
.max_packet_size_0(64)
158+
.build();
159+
160+
interrupt_free(|cs| {
161+
USB_DEVICE.borrow(cs).replace(Some(usb_dev));
162+
SERIAL_PORT.borrow(cs).replace(Some(usb_serial));
163+
});
164+
165+
loop {
166+
cortex_m::asm::nop();
167+
}
168+
}
169+
170+
#[interrupt]
171+
fn OTG_HS() {
172+
interrupt_free(|cs| {
173+
if let (Some(port), Some(device)) = (
174+
SERIAL_PORT.borrow(cs).borrow_mut().as_mut(),
175+
USB_DEVICE.borrow(cs).borrow_mut().as_mut(),
176+
) {
177+
if device.poll(&mut [port]) {
178+
let mut buf = [0u8; 64];
179+
match port.read(&mut buf) {
180+
Ok(count) if count > 0 => {
181+
// Echo back in upper case
182+
for c in buf[0..count].iter_mut() {
183+
if 0x61 <= *c && *c <= 0x7a {
184+
*c &= !0x20;
185+
}
186+
}
187+
188+
let mut write_offset = 0;
189+
while write_offset < count {
190+
match port.write(&buf[write_offset..count]) {
191+
Ok(len) if len > 0 => {
192+
write_offset += len;
193+
}
194+
_ => {}
195+
}
196+
}
197+
}
198+
_ => {}
199+
}
200+
}
201+
}
202+
})
203+
}

examples/usb_rtic.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//! CDC-ACM serial port example using polling in a busy loop
2+
//!
3+
#![deny(warnings)]
4+
#![no_std]
5+
#![no_main]
6+
7+
#[macro_use]
8+
mod utilities;
9+
10+
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)]
11+
mod app {
12+
use stm32h7xx_hal::gpio::gpioe::PE1;
13+
use stm32h7xx_hal::gpio::{Output, PushPull};
14+
use stm32h7xx_hal::hal::digital::v2::OutputPin;
15+
use stm32h7xx_hal::prelude::*;
16+
use stm32h7xx_hal::rcc::rec::UsbClkSel;
17+
use stm32h7xx_hal::usb_hs::{UsbBus, USB1};
18+
use usb_device::prelude::*;
19+
20+
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
21+
use super::utilities;
22+
23+
#[shared]
24+
struct SharedResources {}
25+
#[local]
26+
struct LocalResources {
27+
usb: (
28+
UsbDevice<'static, UsbBus<USB1>>,
29+
usbd_serial::SerialPort<'static, UsbBus<USB1>>,
30+
),
31+
led: PE1<Output<PushPull>>,
32+
}
33+
34+
#[init]
35+
fn init(
36+
ctx: init::Context,
37+
) -> (SharedResources, LocalResources, init::Monotonics) {
38+
utilities::logger::init();
39+
let pwr = ctx.device.PWR.constrain();
40+
let pwrcfg = example_power!(pwr).freeze();
41+
42+
// RCC
43+
let rcc = ctx.device.RCC.constrain();
44+
let mut ccdr = rcc.sys_ck(80.mhz()).freeze(pwrcfg, &ctx.device.SYSCFG);
45+
46+
// 48MHz CLOCK
47+
let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run");
48+
ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::HSI48);
49+
50+
// If your hardware uses the internal USB voltage regulator in ON mode, you
51+
// should uncomment this block.
52+
// unsafe {
53+
// let pwr = &*stm32::PWR::ptr();
54+
// pwr.cr3.modify(|_, w| w.usbregen().set_bit());
55+
// while pwr.cr3.read().usb33rdy().bit_is_clear() {}
56+
// }
57+
58+
// IO
59+
#[cfg(any(feature = "rm0433", feature = "rm0399"))]
60+
let (pin_dm, pin_dp) = {
61+
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
62+
(
63+
gpiob.pb14.into_alternate_af12(),
64+
gpiob.pb15.into_alternate_af12(),
65+
)
66+
};
67+
68+
#[cfg(any(feature = "rm0455", feature = "rm0468"))]
69+
let (pin_dm, pin_dp) = {
70+
let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA);
71+
(
72+
gpioa.pa11.into_alternate_af10(),
73+
gpioa.pa12.into_alternate_af10(),
74+
)
75+
};
76+
77+
let led = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE).pe1;
78+
let usb = USB1::new(
79+
ctx.device.OTG1_HS_GLOBAL,
80+
ctx.device.OTG1_HS_DEVICE,
81+
ctx.device.OTG1_HS_PWRCLK,
82+
pin_dm,
83+
pin_dp,
84+
ccdr.peripheral.USB1OTG,
85+
&ccdr.clocks,
86+
);
87+
88+
let usb_bus = cortex_m::singleton!(
89+
: usb_device::class_prelude::UsbBusAllocator<UsbBus<USB1>> =
90+
UsbBus::new(usb, unsafe { &mut EP_MEMORY })
91+
)
92+
.unwrap();
93+
let serial = usbd_serial::SerialPort::new(usb_bus);
94+
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
95+
.manufacturer("Fake company")
96+
.product("Serial port")
97+
.serial_number("TEST")
98+
.device_class(usbd_serial::USB_CLASS_CDC)
99+
.build();
100+
let usb = (usb_dev, serial);
101+
102+
(
103+
SharedResources {},
104+
LocalResources {
105+
usb,
106+
led: led.into_push_pull_output(),
107+
},
108+
init::Monotonics(),
109+
)
110+
}
111+
112+
#[task(binds = OTG_HS, local = [usb,led])]
113+
fn usb_event(mut ctx: usb_event::Context) {
114+
let (usb_dev, serial) = &mut ctx.local.usb;
115+
ctx.local.led.set_high().ok();
116+
117+
loop {
118+
if !usb_dev.poll(&mut [serial]) {
119+
ctx.local.led.set_low().ok();
120+
return;
121+
}
122+
123+
let mut buf = [0u8; 64];
124+
125+
match serial.read(&mut buf) {
126+
Ok(count) if count > 0 => {
127+
// Echo back in upper case
128+
for c in buf[0..count].iter_mut() {
129+
if 0x61 <= *c && *c <= 0x7a {
130+
*c &= !0x20;
131+
}
132+
}
133+
134+
let mut write_offset = 0;
135+
while write_offset < count {
136+
match serial.write(&buf[write_offset..count]) {
137+
Ok(len) if len > 0 => {
138+
write_offset += len;
139+
}
140+
_ => {}
141+
}
142+
}
143+
}
144+
_ => {}
145+
}
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)