embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.43k stars 750 forks source link

How to set USB Pullup in SYSCFG_PMC on L1s? #2449

Open shufps opened 9 months ago

shufps commented 9 months ago

Hi,

image

The L1s have an internal Pull-Up that needs to be enabled. Without enumeration doesn't work.

I was searching through embassy but couldn't find out how to enable it.

Please, has someone an idea? :pleading_face:

Thx in advance!

shufps commented 9 months ago

I found out I can add this to the usb stm32 driver:

crate::pac::SYSCFG.pmc().modify(|w| w.set_usb_pu(true));

but my USB device still doesn't work with the L1 :disappointed:

I have GND level on D+ and D- ... maybe a sign something is wrong with the clock.

It also hangs here:

0.253234 TRACE usb: power detected
0.253265 TRACE RESET
0.253356 TRACE usb: reset
0.253417 TRACE SETUP read waiting
0.253479 TRACE wait_enabled OUT WAITING
0.253540 TRACE wait_enabled OUT WAITING
0.253601 TRACE wait_enabled IN WAITING

It's waiting for something :thinking:

Dirbaio commented 9 months ago

have you configured the clocks correctly?

RM0038 page 132 6.2.4:

If the USB or SDIO interface is used in the application, the PLL VCO clock (defined by the PLL multiplication factor) must be programmed to output a 96 MHz frequency. This is required to provide a 48 MHz clock to the USB or SDIO (SDIOCLK or USBCLK = PLLVCO/2).

shufps commented 9 months ago

Yes, I'm 99.95% sure that I did it right and I looked over it a dozen times :see_no_evil:

    let mut config = embassy_stm32::Config::default();
    config.rcc.mux = ClockSrc::PLL1_R;
    config.rcc.hse = Some(Hse {
        freq: Hertz::mhz(8),
        mode: HseMode::Bypass,
    });
    config.rcc.pll = Some(Pll {
        source: PllSource::HSE,
        div: PllDiv::DIV3,
        mul: PllMul::MUL12,
    });

    let p = embassy_stm32::init(config);

I use CubeMX as reference for hardware settings:

image

In the meantime I tried C code with HAL and it works, so I can exclude a hardware defect :thinking:

Dirbaio commented 9 months ago

are you sure your board has a 8Mhz HSE? Is it a digital clock instead of an oscillator?

you could also try HSI. It's not accurate enough to meet USB spec but it usually works anyway. If it works then it gives you a clue something's wrong with HSE, at least.


let mut config = embassy_stm32::Config::default();
{
    use embassy_stm32::rcc::*;
    config.rcc.hsi = true;
    config.rcc.pll = Some(Pll {
        source: PllSource::HSI,
        mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz
        div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)
    });
    config.rcc.mux = ClockSrc::PLL1_R;
}
let p = embassy_stm32::init(config);
shufps commented 9 months ago

are you sure your board has a 8Mhz HSE? Is it a digital clock instead of an oscillator?

Yes, it's a custom board, it has an 8MHz oscillator. (not just a crystal, it has a real oscillator that requires bypass clock config, otherwise clock is too slow)

you could also try HSI. It's not accurate enough to meet USB spec but it usually works anyway. If it works then it gives you a clue something's wrong with HSE, at least.

let mut config = embassy_stm32::Config::default();
{
    use embassy_stm32::rcc::*;
    config.rcc.hsi = true;
    config.rcc.pll = Some(Pll {
        source: PllSource::HSI,
        mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz
        div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)
    });
    config.rcc.mux = ClockSrc::PLL1_R;
}
let p = embassy_stm32::init(config);

Hmm yes right, at least I could try it if it changes something ... :thinking:

shufps commented 9 months ago

Hmm, it's the same with HSI :pleading_face:

This is my code:

#![no_std]
#![no_main]

use defmt::{panic, *};
use embassy_executor::Spawner;
use embassy_stm32::rcc::Hse;
use embassy_stm32::rcc::HseMode;
use embassy_stm32::rcc::PllDiv;
use embassy_stm32::rcc::PllMul;
use embassy_stm32::rcc::{ClockSrc, Pll, PllSource};
use embassy_stm32::time::Hertz;
use embassy_stm32::usb::{self, Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::Builder;
use futures::future::join;
use {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct Irqs {
    USB_LP => usb::InterruptHandler<peripherals::USB>;

});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    /*
        let mut config = embassy_stm32::Config::default();
        config.rcc.mux = ClockSrc::PLL1_R;
        config.rcc.hse = Some(Hse {
            freq: Hertz::mhz(8),
            mode: HseMode::Bypass,
        });
        config.rcc.pll = Some(Pll {
            source: PllSource::HSE,
            div: PllDiv::DIV3,
            mul: PllMul::MUL12,
        });
    */
    let mut config = embassy_stm32::Config::default();
    {
        use embassy_stm32::rcc::*;
        config.rcc.hsi = true;
        config.rcc.pll = Some(Pll {
            source: PllSource::HSI,
            mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz
            div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)
        });
        config.rcc.mux = ClockSrc::PLL1_R;
    }

    let p = embassy_stm32::init(config);

    info!("Hello World!");

    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);

    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
    config.manufacturer = Some("Embassy");
    config.product = Some("USB-Serial Example");
    config.serial_number = Some("123456");

    config.device_class = 0xEF;
    config.device_sub_class = 0x02;
    config.device_protocol = 0x01;
    config.composite_with_iads = true;

    let mut device_descriptor = [0; 256];
    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 device_descriptor,
        &mut config_descriptor,
        &mut bos_descriptor,
        &mut [], // no msos descriptors
        &mut control_buf,
    );

    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);

    let mut usb = builder.build();

    let usb_fut = usb.run();

    let echo_fut = async {
        loop {
            class.wait_connection().await;
            info!("Connected");
            let _ = echo(&mut class).await;
            info!("Disconnected");
        }
    };

    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?;
    }
}

It's a bit different to the other usb_serial versions because it uses this weird USB_LP interrupt :thinking:

Dirbaio commented 9 months ago

can you try pulling D+ down on boot, to force reenumeration?

https://github.com/embassy-rs/embassy/blob/main/examples/stm32f1/src/bin/usb_serial.rs#L31-L38

shufps commented 9 months ago

Oh wait, it started working ... I think I found it ...

https://github.com/embassy-rs/embassy/blob/main/embassy-stm32/src/usb/usb.rs#L446

Here this is missing for the L1

crate::pac::SYSCFG.pmc().modify(|w| w.set_usb_pu(true));

And this seems to not be working for the L1 - removing both lines and it started working: https://github.com/embassy-rs/embassy/blob/main/embassy-stm32/src/usb/usb.rs#L289

The AF-Values are 0 - I tried with 10 but it didn't work either :man_shrugging: But removing both works.

Will do more tests tomorrow and then try to add for the L1

HSE is working too :partying_face:

Dirbaio commented 9 months ago

nice!! :tada:

And this seems to not be working for the L1 - removing both lines and it started working: https://github.com/embassy-rs/embassy/blob/main/embassy-stm32/src/usb/usb.rs#L289

That's very strange, I've never seen any STM32 where you don't have to set DP/DM pins as AF! ... but if it works then ... :shrug: