aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-12-13 11:06:32 +0000
committerGitHub <[email protected]>2024-12-13 11:06:32 +0000
commite44ee26cd5bfdc0265097dec1f001257d3119028 (patch)
tree19850833a9dd68c9cd64f5f27d9ecc32113f77d1
parent406d377b7564d16e12b7fae4f42c0c709bf4f243 (diff)
parenta0e056a629c166b38c536c68afbf852819f10143 (diff)
Merge pull request #3613 from mubes/stm32u5_otg_hs_support
Add support for stm32u595/5a5 OTG_HS in client mode
-rw-r--r--embassy-stm32/src/rcc/u5.rs29
-rw-r--r--embassy-stm32/src/usb/mod.rs14
-rw-r--r--embassy-stm32/src/usb/otg.rs30
-rw-r--r--examples/stm32u5/src/bin/usb_hs_serial.rs129
4 files changed, 193 insertions, 9 deletions
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs
index af99c77bc..dc77dc540 100644
--- a/embassy-stm32/src/rcc/u5.rs
+++ b/embassy-stm32/src/rcc/u5.rs
@@ -1,9 +1,13 @@
1pub use crate::pac::pwr::vals::Vos as VoltageScale; 1pub use crate::pac::pwr::vals::Vos as VoltageScale;
2#[cfg(all(peri_usb_otg_hs))]
3pub use crate::pac::rcc::vals::Otghssel;
2pub use crate::pac::rcc::vals::{ 4pub use crate::pac::rcc::vals::{
3 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, 5 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
4 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, 6 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
5}; 7};
6use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; 8use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge};
9#[cfg(all(peri_usb_otg_hs))]
10pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
7use crate::pac::{FLASH, PWR, RCC}; 11use crate::pac::{FLASH, PWR, RCC};
8use crate::rcc::LSI_FREQ; 12use crate::rcc::LSI_FREQ;
9use crate::time::Hertz; 13use crate::time::Hertz;
@@ -295,6 +299,31 @@ pub(crate) unsafe fn init(config: Config) {
295 299
296 let rtc = config.ls.init(); 300 let rtc = config.ls.init();
297 301
302 #[cfg(all(stm32u5, peri_usb_otg_hs))]
303 let usb_refck = match config.mux.otghssel {
304 Otghssel::HSE => hse,
305 Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
306 Otghssel::PLL1_P => pll1.p,
307 Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),
308 };
309 #[cfg(all(stm32u5, peri_usb_otg_hs))]
310 let usb_refck_sel = match usb_refck {
311 Some(clk_val) => match clk_val {
312 Hertz(16_000_000) => Usbrefcksel::MHZ16,
313 Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
314 Hertz(20_000_000) => Usbrefcksel::MHZ20,
315 Hertz(24_000_000) => Usbrefcksel::MHZ24,
316 Hertz(26_000_000) => Usbrefcksel::MHZ26,
317 Hertz(32_000_000) => Usbrefcksel::MHZ32,
318 _ => panic!("cannot select OTG_HS reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val),
319 },
320 None => Usbrefcksel::MHZ24,
321 };
322 #[cfg(all(stm32u5, peri_usb_otg_hs))]
323 SYSCFG.otghsphycr().modify(|w| {
324 w.set_clksel(usb_refck_sel);
325 });
326
298 let lse = config.ls.lse.map(|l| l.frequency); 327 let lse = config.ls.lse.map(|l| l.frequency);
299 let lsi = config.ls.lsi.then_some(LSI_FREQ); 328 let lsi = config.ls.lsi.then_some(LSI_FREQ);
300 329
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs
index a473285bf..ae5963420 100644
--- a/embassy-stm32/src/usb/mod.rs
+++ b/embassy-stm32/src/usb/mod.rs
@@ -15,7 +15,7 @@ fn common_init<T: Instance>() {
15 let freq = T::frequency(); 15 let freq = T::frequency();
16 16
17 // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally 17 // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally
18 #[cfg(stm32h7rs)] 18 #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))]
19 if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { 19 if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
20 panic!( 20 panic!(
21 "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", 21 "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.",
@@ -25,7 +25,7 @@ fn common_init<T: Instance>() {
25 // Check frequency is within the 0.25% tolerance allowed by the spec. 25 // Check frequency is within the 0.25% tolerance allowed by the spec.
26 // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user 26 // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
27 // has tight clock restrictions due to something else (like audio). 27 // has tight clock restrictions due to something else (like audio).
28 #[cfg(not(stm32h7rs))] 28 #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))]
29 if freq.0.abs_diff(48_000_000) > 120_000 { 29 if freq.0.abs_diff(48_000_000) > 120_000 {
30 panic!( 30 panic!(
31 "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", 31 "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
@@ -90,6 +90,16 @@ fn common_init<T: Instance>() {
90 90
91 // Wait for USB power to stabilize 91 // Wait for USB power to stabilize
92 while !crate::pac::PWR.svmsr().read().vddusbrdy() {} 92 while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
93
94 // Now set up transceiver power if it's a OTG-HS
95 #[cfg(peri_usb_otg_hs)]
96 {
97 crate::pac::PWR.vosr().modify(|w| {
98 w.set_usbpwren(true);
99 w.set_usbboosten(true);
100 });
101 while !crate::pac::PWR.vosr().read().usbboostrdy() {}
102 }
93 } 103 }
94 104
95 T::Interrupt::unpend(); 105 T::Interrupt::unpend();
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs
index 6ca28b156..d3c7978e4 100644
--- a/embassy-stm32/src/usb/otg.rs
+++ b/embassy-stm32/src/usb/otg.rs
@@ -26,7 +26,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
26 unsafe fn on_interrupt() { 26 unsafe fn on_interrupt() {
27 let r = T::regs(); 27 let r = T::regs();
28 let state = T::state(); 28 let state = T::state();
29
30 on_interrupt_impl(r, state, T::ENDPOINT_COUNT); 29 on_interrupt_impl(r, state, T::ENDPOINT_COUNT);
31 } 30 }
32} 31}
@@ -103,15 +102,18 @@ impl<'d, T: Instance> Driver<'d, T> {
103 pub fn new_hs( 102 pub fn new_hs(
104 _peri: impl Peripheral<P = T> + 'd, 103 _peri: impl Peripheral<P = T> + 'd,
105 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, 104 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
106 dp: impl Peripheral<P = impl DpPin<T>> + 'd, 105 _dp: impl Peripheral<P = impl DpPin<T>> + 'd,
107 dm: impl Peripheral<P = impl DmPin<T>> + 'd, 106 _dm: impl Peripheral<P = impl DmPin<T>> + 'd,
108 ep_out_buffer: &'d mut [u8], 107 ep_out_buffer: &'d mut [u8],
109 config: Config, 108 config: Config,
110 ) -> Self { 109 ) -> Self {
111 into_ref!(dp, dm); 110 // For STM32U5 High speed pins need to be left in analog mode
112 111 #[cfg(not(all(stm32u5, peri_usb_otg_hs)))]
113 dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 112 {
114 dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 113 into_ref!(_dp, _dm);
114 _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
115 _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
116 }
115 117
116 let instance = OtgInstance { 118 let instance = OtgInstance {
117 regs: T::regs(), 119 regs: T::regs(),
@@ -311,6 +313,20 @@ impl<'d, T: Instance> Bus<'d, T> {
311 }); 313 });
312 }); 314 });
313 315
316 #[cfg(all(stm32u5, peri_usb_otg_hs))]
317 {
318 crate::pac::SYSCFG.otghsphycr().modify(|w| {
319 w.set_en(true);
320 });
321
322 critical_section::with(|_| {
323 crate::pac::RCC.ahb2enr1().modify(|w| {
324 w.set_usb_otg_hsen(true);
325 w.set_usb_otg_hs_phyen(true);
326 });
327 });
328 }
329
314 let r = T::regs(); 330 let r = T::regs();
315 let core_id = r.cid().read().0; 331 let core_id = r.cid().read().0;
316 trace!("Core id {:08x}", core_id); 332 trace!("Core id {:08x}", core_id);
diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs
new file mode 100644
index 000000000..5549e2cbb
--- /dev/null
+++ b/examples/stm32u5/src/bin/usb_hs_serial.rs
@@ -0,0 +1,129 @@
1#![no_std]
2#![no_main]
3
4use defmt::{panic, *};
5use defmt_rtt as _; // global logger
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_stm32::usb::{Driver, Instance};
9use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use panic_probe as _;
14
15bind_interrupts!(struct Irqs {
16 OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 info!("Hello World!");
22
23 let mut config = Config::default();
24 {
25 use embassy_stm32::rcc::*;
26 use embassy_stm32::time::Hertz;
27 config.rcc.hse = Some(Hse {
28 freq: Hertz(16_000_000),
29 mode: HseMode::Oscillator,
30 });
31 config.rcc.pll1 = Some(Pll {
32 source: PllSource::HSE,
33 prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz
34 mul: PllMul::MUL60, // 8MHz * 60 = 480MHz
35 divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck)
36 divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)
37 divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)
38 });
39 config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
40 config.rcc.voltage_range = VoltageScale::RANGE1;
41 config.rcc.sys = Sysclk::PLL1_R;
42 }
43
44 let p = embassy_stm32::init(config);
45
46 // Create the driver, from the HAL.
47 let mut ep_out_buffer = [0u8; 256];
48 let mut config = embassy_stm32::usb::Config::default();
49 // Do not enable vbus_detection. This is a safe default that works in all boards.
50 // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
51 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
52 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
53 config.vbus_detection = false;
54 let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
55
56 // Create embassy-usb Config
57 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
58 config.manufacturer = Some("Embassy");
59 config.product = Some("USB-serial example");
60 config.serial_number = Some("12345678");
61
62 // Required for windows compatibility.
63 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
64 config.device_class = 0xEF;
65 config.device_sub_class = 0x02;
66 config.device_protocol = 0x01;
67 config.composite_with_iads = true;
68
69 // Create embassy-usb DeviceBuilder using the driver and config.
70 // It needs some buffers for building the descriptors.
71 let mut config_descriptor = [0; 256];
72 let mut bos_descriptor = [0; 256];
73 let mut control_buf = [0; 64];
74
75 let mut state = State::new();
76
77 let mut builder = Builder::new(
78 driver,
79 config,
80 &mut config_descriptor,
81 &mut bos_descriptor,
82 &mut [], // no msos descriptors
83 &mut control_buf,
84 );
85
86 // Create classes on the builder.
87 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
88
89 // Build the builder.
90 let mut usb = builder.build();
91
92 // Run the USB device.
93 let usb_fut = usb.run();
94
95 // Do stuff with the class!
96 let echo_fut = async {
97 loop {
98 class.wait_connection().await;
99 info!("Connected");
100 let _ = echo(&mut class).await;
101 info!("Disconnected");
102 }
103 };
104
105 // Run everything concurrently.
106 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
107 join(usb_fut, echo_fut).await;
108}
109
110struct Disconnected {}
111
112impl From<EndpointError> for Disconnected {
113 fn from(val: EndpointError) -> Self {
114 match val {
115 EndpointError::BufferOverflow => panic!("Buffer overflow"),
116 EndpointError::Disabled => Disconnected {},
117 }
118 }
119}
120
121async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
122 let mut buf = [0; 64];
123 loop {
124 let n = class.read_packet(&mut buf).await?;
125 let data = &buf[..n];
126 info!("data: {:x}", data);
127 class.write_packet(data).await?;
128 }
129}