Merge pull request #3613 from mubes/stm32u5_otg_hs_support
Add support for stm32u595/5a5 OTG_HS in client mode
This commit is contained in:
commit
e44ee26cd5
@ -1,9 +1,13 @@
|
|||||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||||
|
#[cfg(all(peri_usb_otg_hs))]
|
||||||
|
pub use crate::pac::rcc::vals::Otghssel;
|
||||||
pub use crate::pac::rcc::vals::{
|
pub use crate::pac::rcc::vals::{
|
||||||
Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
|
Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
|
||||||
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
|
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
|
||||||
};
|
};
|
||||||
use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge};
|
use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge};
|
||||||
|
#[cfg(all(peri_usb_otg_hs))]
|
||||||
|
pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
use crate::rcc::LSI_FREQ;
|
use crate::rcc::LSI_FREQ;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -295,6 +299,31 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
let rtc = config.ls.init();
|
let rtc = config.ls.init();
|
||||||
|
|
||||||
|
#[cfg(all(stm32u5, peri_usb_otg_hs))]
|
||||||
|
let usb_refck = match config.mux.otghssel {
|
||||||
|
Otghssel::HSE => hse,
|
||||||
|
Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
|
||||||
|
Otghssel::PLL1_P => pll1.p,
|
||||||
|
Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),
|
||||||
|
};
|
||||||
|
#[cfg(all(stm32u5, peri_usb_otg_hs))]
|
||||||
|
let usb_refck_sel = match usb_refck {
|
||||||
|
Some(clk_val) => match clk_val {
|
||||||
|
Hertz(16_000_000) => Usbrefcksel::MHZ16,
|
||||||
|
Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
|
||||||
|
Hertz(20_000_000) => Usbrefcksel::MHZ20,
|
||||||
|
Hertz(24_000_000) => Usbrefcksel::MHZ24,
|
||||||
|
Hertz(26_000_000) => Usbrefcksel::MHZ26,
|
||||||
|
Hertz(32_000_000) => Usbrefcksel::MHZ32,
|
||||||
|
_ => 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),
|
||||||
|
},
|
||||||
|
None => Usbrefcksel::MHZ24,
|
||||||
|
};
|
||||||
|
#[cfg(all(stm32u5, peri_usb_otg_hs))]
|
||||||
|
SYSCFG.otghsphycr().modify(|w| {
|
||||||
|
w.set_clksel(usb_refck_sel);
|
||||||
|
});
|
||||||
|
|
||||||
let lse = config.ls.lse.map(|l| l.frequency);
|
let lse = config.ls.lse.map(|l| l.frequency);
|
||||||
let lsi = config.ls.lsi.then_some(LSI_FREQ);
|
let lsi = config.ls.lsi.then_some(LSI_FREQ);
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ fn common_init<T: Instance>() {
|
|||||||
let freq = T::frequency();
|
let freq = T::frequency();
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
#[cfg(stm32h7rs)]
|
#[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))]
|
||||||
if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
|
if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
|
||||||
panic!(
|
panic!(
|
||||||
"USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.",
|
"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>() {
|
|||||||
// Check frequency is within the 0.25% tolerance allowed by the spec.
|
// Check frequency is within the 0.25% tolerance allowed by the spec.
|
||||||
// Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
|
// Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
|
||||||
// has tight clock restrictions due to something else (like audio).
|
// has tight clock restrictions due to something else (like audio).
|
||||||
#[cfg(not(stm32h7rs))]
|
#[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))]
|
||||||
if freq.0.abs_diff(48_000_000) > 120_000 {
|
if freq.0.abs_diff(48_000_000) > 120_000 {
|
||||||
panic!(
|
panic!(
|
||||||
"USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
|
"USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
|
||||||
@ -90,6 +90,16 @@ fn common_init<T: Instance>() {
|
|||||||
|
|
||||||
// Wait for USB power to stabilize
|
// Wait for USB power to stabilize
|
||||||
while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
|
while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
|
||||||
|
|
||||||
|
// Now set up transceiver power if it's a OTG-HS
|
||||||
|
#[cfg(peri_usb_otg_hs)]
|
||||||
|
{
|
||||||
|
crate::pac::PWR.vosr().modify(|w| {
|
||||||
|
w.set_usbpwren(true);
|
||||||
|
w.set_usbboosten(true);
|
||||||
|
});
|
||||||
|
while !crate::pac::PWR.vosr().read().usbboostrdy() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T::Interrupt::unpend();
|
T::Interrupt::unpend();
|
||||||
|
|||||||
@ -26,7 +26,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
let state = T::state();
|
let state = T::state();
|
||||||
|
|
||||||
on_interrupt_impl(r, state, T::ENDPOINT_COUNT);
|
on_interrupt_impl(r, state, T::ENDPOINT_COUNT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,15 +102,18 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||||||
pub fn new_hs(
|
pub fn new_hs(
|
||||||
_peri: impl Peripheral<P = T> + 'd,
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
_dp: impl Peripheral<P = impl DpPin<T>> + 'd,
|
||||||
dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
_dm: impl Peripheral<P = impl DmPin<T>> + 'd,
|
||||||
ep_out_buffer: &'d mut [u8],
|
ep_out_buffer: &'d mut [u8],
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(dp, dm);
|
// For STM32U5 High speed pins need to be left in analog mode
|
||||||
|
#[cfg(not(all(stm32u5, peri_usb_otg_hs)))]
|
||||||
dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
|
{
|
||||||
dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
|
into_ref!(_dp, _dm);
|
||||||
|
_dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
|
||||||
|
_dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
|
||||||
|
}
|
||||||
|
|
||||||
let instance = OtgInstance {
|
let instance = OtgInstance {
|
||||||
regs: T::regs(),
|
regs: T::regs(),
|
||||||
@ -311,6 +313,20 @@ impl<'d, T: Instance> Bus<'d, T> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(all(stm32u5, peri_usb_otg_hs))]
|
||||||
|
{
|
||||||
|
crate::pac::SYSCFG.otghsphycr().modify(|w| {
|
||||||
|
w.set_en(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
critical_section::with(|_| {
|
||||||
|
crate::pac::RCC.ahb2enr1().modify(|w| {
|
||||||
|
w.set_usb_otg_hsen(true);
|
||||||
|
w.set_usb_otg_hs_phyen(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
let core_id = r.cid().read().0;
|
let core_id = r.cid().read().0;
|
||||||
trace!("Core id {:08x}", core_id);
|
trace!("Core id {:08x}", core_id);
|
||||||
|
|||||||
129
examples/stm32u5/src/bin/usb_hs_serial.rs
Normal file
129
examples/stm32u5/src/bin/usb_hs_serial.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::{panic, *};
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_futures::join::join;
|
||||||
|
use embassy_stm32::usb::{Driver, Instance};
|
||||||
|
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
|
||||||
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
|
use embassy_usb::driver::EndpointError;
|
||||||
|
use embassy_usb::Builder;
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let mut config = Config::default();
|
||||||
|
{
|
||||||
|
use embassy_stm32::rcc::*;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
config.rcc.hse = Some(Hse {
|
||||||
|
freq: Hertz(16_000_000),
|
||||||
|
mode: HseMode::Oscillator,
|
||||||
|
});
|
||||||
|
config.rcc.pll1 = Some(Pll {
|
||||||
|
source: PllSource::HSE,
|
||||||
|
prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz
|
||||||
|
mul: PllMul::MUL60, // 8MHz * 60 = 480MHz
|
||||||
|
divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck)
|
||||||
|
divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)
|
||||||
|
divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)
|
||||||
|
});
|
||||||
|
config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
|
||||||
|
config.rcc.voltage_range = VoltageScale::RANGE1;
|
||||||
|
config.rcc.sys = Sysclk::PLL1_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
|
// Create the driver, from the HAL.
|
||||||
|
let mut ep_out_buffer = [0u8; 256];
|
||||||
|
let mut config = embassy_stm32::usb::Config::default();
|
||||||
|
// Do not enable vbus_detection. This is a safe default that works in all boards.
|
||||||
|
// However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
|
||||||
|
// to enable vbus_detection to comply with the USB spec. If you enable it, the board
|
||||||
|
// has to support it or USB won't work at all. See docs on `vbus_detection` for details.
|
||||||
|
config.vbus_detection = false;
|
||||||
|
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
||||||
|
|
||||||
|
// Create embassy-usb Config
|
||||||
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-serial example");
|
||||||
|
config.serial_number = Some("12345678");
|
||||||
|
|
||||||
|
// Required for windows compatibility.
|
||||||
|
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||||
|
config.device_class = 0xEF;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x01;
|
||||||
|
config.composite_with_iads = true;
|
||||||
|
|
||||||
|
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||||
|
// It needs some buffers for building the descriptors.
|
||||||
|
let mut config_descriptor = [0; 256];
|
||||||
|
let mut bos_descriptor = [0; 256];
|
||||||
|
let mut control_buf = [0; 64];
|
||||||
|
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut config_descriptor,
|
||||||
|
&mut bos_descriptor,
|
||||||
|
&mut [], // no msos descriptors
|
||||||
|
&mut control_buf,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create classes on the builder.
|
||||||
|
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
|
||||||
|
|
||||||
|
// Build the builder.
|
||||||
|
let mut usb = builder.build();
|
||||||
|
|
||||||
|
// Run the USB device.
|
||||||
|
let usb_fut = usb.run();
|
||||||
|
|
||||||
|
// Do stuff with the class!
|
||||||
|
let echo_fut = async {
|
||||||
|
loop {
|
||||||
|
class.wait_connection().await;
|
||||||
|
info!("Connected");
|
||||||
|
let _ = echo(&mut class).await;
|
||||||
|
info!("Disconnected");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run everything concurrently.
|
||||||
|
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
|
||||||
|
join(usb_fut, echo_fut).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Disconnected {}
|
||||||
|
|
||||||
|
impl From<EndpointError> for Disconnected {
|
||||||
|
fn from(val: EndpointError) -> Self {
|
||||||
|
match val {
|
||||||
|
EndpointError::BufferOverflow => panic!("Buffer overflow"),
|
||||||
|
EndpointError::Disabled => Disconnected {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
loop {
|
||||||
|
let n = class.read_packet(&mut buf).await?;
|
||||||
|
let data = &buf[..n];
|
||||||
|
info!("data: {:x}", data);
|
||||||
|
class.write_packet(data).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user