Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB ID 4000 on STM32H7R/S? #3289

Open
RileyLeff opened this issue Aug 26, 2024 · 6 comments
Open

USB ID 4000 on STM32H7R/S? #3289

RileyLeff opened this issue Aug 26, 2024 · 6 comments

Comments

@RileyLeff
Copy link

Howdy folks, hoping someone can point me in the right direction re: USB core IDs.

I spent the morning fiddling with the STM32H7 usb_serial example to get it to run on my STM32H7R/S board (this relatively new nucleo with a STM32H7S3L8H MCU), as there isn't a USB example for the R/S boards yet.

Changing some of the prescaler values in the initial config and pin #s got it to compile, but it would panic at runtime with this message:

not implemented: Unknown USB core id 4000

I added 0x0000_4000 to the core_id match statement in the relevant file here, and just gave it the same config as the 200x and 300x ids. That seems to run the example just fine without any panic.

Would it be appropriate to submit a PR adding my example and appending 0x_4000 to that match statement with a v2_v3 config? Or are there additional considerations for core ID config beyond "this basic example works" that need to be addressed before adding support for a 4000 usb ID?

@Dirbaio
Copy link
Member

Dirbaio commented Aug 26, 2024

yes, please send a PR!

treating v4 the same as v2/v3 should be OK. Checking the core version is mostly used to workaround weird quirks in v1, if you tried that and it works then it probably means there's no quirks we should care about in v4.

@RileyLeff
Copy link
Author

Turns out this was not working as I thought -- while it compiles, runs, and prints a lot of very beautiful INFOs, the usb FS-enabled pins don't actually exist on my board. Whoops. It compiles just fine if I switch it over to the HS pins (which I've confirmed are actually hooked up to the USB port this time, lol) but I'm getting a runtime panic that is completely stumping me:

ERROR panicked at 'peripheral 'USB_OTG_HS' is configured to use the 'USB' clock, which is not running. Either enable it in 'config.rcc' or change 'config.rcc.mux' to use another clock'

I don't see usb as a field in rcc or mux. Any idea how I can set up that clock? I see usb in the rcc clocks module but no idea how to get it into the config.

Also, I see there's only a new_fs function under Driver::, not a new_hs. I'm assuming that's fine, as a HS interface can run at FS speeds, unless I'm misunderstanding something? I don't need full HS data rates out of this thing, FS would be fine.

Here's the rest of my code for context, which I'm running from inside examples/stm32h7rs/src/bin:

`#![no_std]
#![no_main]

use defmt::{panic, *};
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 {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct Irqs {
OTG_HS => usb::InterruptHandlerperipherals::USB_OTG_HS;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");

let mut config = Config::default();

{
    use embassy_stm32::rcc::*;
    config.rcc.hsi = Some(HSIPrescaler::DIV1);
    config.rcc.csi = true;
    config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
    config.rcc.pll1 = Some(Pll {
        source: PllSource::HSI,
        prediv: PllPreDiv::DIV4,
        mul: PllMul::MUL50,
        divp: Some(PllDiv::DIV2),
        divq: None,
        divr: None,
    });
    config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
    config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
    config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
    config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
    //config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
    config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
    config.rcc.apb5_pre = APBPrescaler::DIV2;
    config.rcc.voltage_scale = VoltageScale::HIGH;
}

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();

config.vbus_detection = false;

let driver = Driver::new_fs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &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;

info!("RILEYHERE");

// 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();

info!("RILEYHERE2");

// Do stuff with the class!
let echo_fut = async {
    loop {
        info!("RILEYHERE3");
        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 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?;
}
}`

@RileyLeff
Copy link
Author

gonna try adding this bad boy to the config when i get back to the lab tomorrow

config.rcc.mux.usb_otg_fssel = mux::UsbOtgFssel::HSI48; // fix USB?

if it doesn't work i'm getting a consolation shawarma

if it works i'm getting a celebration shawarma

@RileyLeff
Copy link
Author

RileyLeff commented Aug 27, 2024

consolation shawarma it is, still broken with the same runtime error:

ERROR panicked at 'peripheral 'USB_OTG_HS' is configured to use the 'USB' clock, which is not running. Either enable it in 'config.rcc' or change 'config.rcc.mux' to use another clock'.

Any direction from anybody would be appreciated!

@doesnotcompete
Copy link

Hey, I'm trying to get USB device mode working on a Nucleo-H7S3 as well.

The problem might be the missing USB clock in the RCC configuration structure:

usb: None, // TODO

I've set this to 48MHz (usb: Some(Hertz(48_000_000))) and made sure the PHY PLL and USB clocks are running:

RCC.ahb1enr().modify(|w|
  w.set_usb_otg_hsen(true);
  w.set_usbphycen(true);
});
PWR.csr2().modify(|w| {
  w.set_usbregen(true);
  w.set_usb33den(true);
  w.set_usbhsregen(true);
});

Ultimately this should probably be integrated in the RCC and USB init functions.

I've also adapted your clock configuration like this to configure the HSE:

    {
        use embassy_stm32::rcc::*;
        config.rcc.hse = Some(Hse { freq: Hertz(24_000_000), mode: HseMode::Oscillator });
        config.rcc.hsi = Some(HSIPrescaler::DIV1);
        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });
        config.rcc.pll1 = Some(Pll {
            source: PllSource::HSE,
            prediv: PllPreDiv::DIV12,
            mul: PllMul::MUL300,
            divp: Some(PllDiv::DIV1), //600 MHz
            divq: Some(PllDiv::DIV2), // 300 MHz
            divr: Some(PllDiv::DIV2), // 300 MHz
        });
        config.rcc.sys = Sysclk::PLL1_P; // 600 MHz
        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz
        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz
        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz
        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz
        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz
        config.rcc.voltage_scale = VoltageScale::HIGH;
        config.rcc.mux.usb_otg_fssel = mux::UsbOtgFssel::HSI48;
        config.rcc.pll2 = Some(Pll {
            source: PllSource::HSE,
            prediv: PllPreDiv::DIV12,
            mul: PllMul::MUL200,
            divp: Some(PllDiv::DIV2), // 200 MHz
            divq: Some(PllDiv::DIV2), // 200 MHz
            divr: Some(PllDiv::DIV2), // 200 MHz
        });
    }

By default, the USB PHY Clock Mux (which I didn't find represented anywhere in the embassy RCC configuration yet) should then use the HSE.

I've also set the phy_type in otg.rs to PhyType::InternalHighSpeed, although I'm not sure this changes anything.
Interestingly, for me the core ID seems to be 5000, so I've added 0x0000_5000 to the match statement in otg.rs as well.

With these changes, this is the output I'm getting with your code:

INFO  Hello World!
└─ usb_serial::____embassy_main_task::{async_fn#0} @ src/bin/usb_serial.rs:22  
DEBUG flash: latency=7 wrhighfreq=3
└─ embassy_stm32::rcc::_version::flash_setup @ /home/kevin/dev/embassy/embassy-stm32/src/fmt.rs:138 
TRACE BDCR ok: 00008210
└─ embassy_stm32::rcc::bd::{impl#3}::init @ /home/kevin/dev/embassy/embassy-stm32/src/fmt.rs:124 
DEBUG rcc: Clocks { clk48mohci: MaybeHertz(0), csi: MaybeHertz(0), hclk1: MaybeHertz(300000000), hclk2: MaybeHertz(300000000), hclk3: MaybeHertz(300000000), hclk4: MaybeHertz(300000000), hclk5: MaybeHertz(300000000), hse: MaybeHertz(24000000), hsi: MaybeHertz(64000000), hsi48: MaybeHertz(48000000), i2s_ckin: MaybeHertz(0), lse: MaybeHertz(0), lsi: MaybeHertz(0), pclk1: MaybeHertz(150000000), pclk1_tim: MaybeHertz(300000000), pclk2: MaybeHertz(150000000), pclk2_tim: MaybeHertz(300000000), pclk4: MaybeHertz(150000000), pll1_q: MaybeHertz(300000000), pll2_p: MaybeHertz(200000000), pll2_q: MaybeHertz(200000000), pll2_r: MaybeHertz(200000000), pll2_s: MaybeHertz(0), pll2_t: MaybeHertz(0), pll3_p: MaybeHertz(0), pll3_q: MaybeHertz(0), pll3_r: MaybeHertz(0), rtc: MaybeHertz(32000), spdifrx_symb: MaybeHertz(0), sys: MaybeHertz(600000000), usb: MaybeHertz(48000000) }
└─ embassy_stm32::rcc::set_freqs @ /home/kevin/dev/embassy/embassy-stm32/src/fmt.rs:138 
INFO  RILEYHERE
└─ usb_serial::____embassy_main_task::{async_fn#0} @ src/bin/usb_serial.rs:79  
INFO  USB: config_descriptor used: 70
└─ embassy_usb::builder::{impl#1}::build @ /home/kevin/dev/embassy/embassy-usb/src/fmt.rs:152 
INFO  USB: bos_descriptor used: 12
└─ embassy_usb::builder::{impl#1}::build @ /home/kevin/dev/embassy/embassy-usb/src/fmt.rs:152 
INFO  USB: msos_descriptor used: 0
└─ embassy_usb::builder::{impl#1}::build @ /home/kevin/dev/embassy/embassy-usb/src/fmt.rs:152 
INFO  USB: control_buf size: 64
└─ embassy_usb::builder::{impl#1}::build @ /home/kevin/dev/embassy/embassy-usb/src/fmt.rs:152 
INFO  RILEYHERE2
└─ usb_serial::____embassy_main_task::{async_fn#0} @ src/bin/usb_serial.rs:107 
TRACE Core id 00005000
└─ embassy_stm32::usb::_version::{impl#3}::init @ /home/kevin/dev/embassy/embassy-stm32/src/fmt.rs:124 
TRACE usb: power detected
└─ embassy_usb::{impl#2}::handle_bus_event::{async_fn#0} @ /home/kevin/dev/embassy/embassy-usb/src/fmt.rs:124 
INFO  RILEYHERE3
└─ usb_serial::____embassy_main_task::{async_fn#0}::{async_block#0} @ src/bin/usb_serial.rs:112 
TRACE irq
└─ embassy_stm32::usb::_version::{impl#0}::on_interrupt @ /home/kevin/dev/embassy/embassy-stm32/src/fmt.rs:124

During that first interrupt, the PTXFE, USBRST and NPTXFE flags are set.

Here I am stuck as well though, as there are no further interrupts being generated and the board still does not enumerate.

@doesnotcompete
Copy link

doesnotcompete commented Sep 14, 2024

I've tried comparing the register state of the OTG_HS peripheral subsequent to calling the setup code (but before enumeration) both with Embassy and a working example from the Cube MCU package. I'm attaching the register dumps in case someone else also wants to take a look:
OTG_HS register state from working Cube example
OTG_HS register state from Embassy usb_serial example

So far I didn't get USB to work, but I've noticed that there seem to be some register definitions missing from stm32-metapac for the OTG_GCCFG register. There are currently two versions of that fieldset, but none of them accurately represent the peripheral on the H7RS series (see RM0477 sec. 62.14.18).

I'm not sure how much of an issue this is though. If this was an actual problem, the OTG_HS peripheral shouldn't work on the U5 series either, as that has the same register definitions (cp. RM0456 sec. 73.14.18).

Edit: Apparently someone else has already encountered this issue as well: embassy-rs/stm32-data#519

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@Dirbaio @doesnotcompete @RileyLeff and others