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-otg does not work for STM32L476ZGTx #64

Closed
seanybaggins opened this issue Nov 2, 2022 · 10 comments
Closed

Usb-otg does not work for STM32L476ZGTx #64

seanybaggins opened this issue Nov 2, 2022 · 10 comments
Labels
bug Something isn't working

Comments

@seanybaggins
Copy link
Contributor

Here's the code I am running.

#![no_std]
#![no_main]

use cortex_m::asm;

use stm32_hal2::{
    self,
    clocks::Clocks,
    gpio::{Pin, PinMode, Port},
    pac,
};

use stm32::timers::MonoTimer;

use stm32_hal2::pac::TIM2;

use stm32_hal2::usb_otg::{Usb1, Usb1BusType, UsbBus};
use stm32_usbd::UsbPeripheral;

use usb_device::prelude::*;

use usbd_serial::SerialPort;
//use stm32_usbd::UsbBus;

use defmt_rtt as _;
use panic_probe as _;

const TIMER_FREQ_HZ: u32 = 1_000_000;

#[rtic::app(device = pac, dispatchers = [USART1])]
mod app {

    use usb_device::class_prelude::UsbBusAllocator;
    use usbd_serial::USB_CLASS_CDC;

    // This line is required to allow imports inside an RTIC module.
    use super::*;

    pub struct PeripheralUsb {
        pub serial: SerialPort<'static, UsbBus<Usb1>>,
        pub device: UsbDevice<'static, UsbBus<Usb1>>,
    }

    #[shared]
    struct Shared {
        peripheral_usb: PeripheralUsb,
    }

    #[local]
    struct Local {
        //usb_serial_port: SerialPort<'static, Usb1BusType>,
        //usb_device: UsbDevice<Usb1>,
    }

    #[monotonic(binds = TIM2, default = true)]
    type Mono2 = MonoTimer<TIM2, TIMER_FREQ_HZ>;

    #[init(local = [
           usb_buf: [u32; 64] = [0; 64],
           usb_bus: Option<UsbBusAllocator<UsbBus<Usb1>>> = None,
    ])]
    fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
        let clock_cfg = Clocks {
            ..Default::default()
        };
        clock_cfg.setup().unwrap();
        clock_cfg.enable_msi_48();

        //stm32::usb::enable_crs();
        //stm32::usb::enable_usb_pwr();
        //assert!(false);

        let _usb_dm = Pin::new(Port::A, 11, PinMode::Alt(14));
        let _usb_dp = Pin::new(Port::A, 12, PinMode::Alt(14));

        let usb1 = Usb1::new(
            cx.device.OTG_FS_GLOBAL,
            cx.device.OTG_FS_DEVICE,
            cx.device.OTG_FS_PWRCLK,
            clock_cfg.hclk(),
        );
        let buf: &'static mut _ = cx.local.usb_buf;
        let bus: &'static mut _ = cx.local.usb_bus.insert(UsbBus::new(usb1, buf));
        let serial = SerialPort::new(bus);
        defmt::println!("Does print");
        let device = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd))
            .manufacturer("Fake Company")
            .product("Serial Port")
            .serial_number("SN")
            .device_class(USB_CLASS_CDC)
            .self_powered(true)
            .build();
        defmt::println!("Does not print");
        let peripheral_usb = PeripheralUsb { serial, device };

        let mono_timer2: Mono2 = MonoTimer::new(cx.device.TIM2, &clock_cfg);

        (
            Shared { peripheral_usb },
            Local {},
            init::Monotonics(mono_timer2),
        )
    }

    #[idle()]
    fn idle(_cx: idle::Context) -> ! {
        usb_say_hello::spawn().unwrap();
        loop {
            asm::nop()
        }
    }

    #[task(shared = [peripheral_usb], priority = 1)]
    fn usb_say_hello(cx: usb_say_hello::Context) {
        defmt::println!("usb_say_hello");
        let mut peripheral_usb = cx.shared.peripheral_usb;
        //let &mut usb_device = &mut cx.local.usb_device;
        //let &mut usb_serial_port = &mut cx.local.usb_serial_port;

        peripheral_usb.lock(|PeripheralUsb { device, serial }| loop {
            if !device.poll(&mut [serial]) {
                continue;
            }

            defmt::println!("Something in buffer");

            let mut buf = [0u8; 64];

            match serial.read(&mut buf[..]) {
                Ok(count) => {
                    serial.write(&buf[..count]).unwrap();
                }
                Err(UsbError::WouldBlock) => {
                    panic!("usb buffer full");
                }
                Err(err) => {
                    panic!("{:?}", err);
                }
            }
        })
    }
}

Digging into the build function, it looks like synopsys-usb-otg dependency is trying to modify a register that is not supported for l476 chips.

synopsys-usb-otg/src/bus.rs

            // ...
            // Perform core soft-reset
            while read_reg!(otg_global, regs.global(), GRSTCTL, AHBIDL) == 0 {}
            modify_reg!(otg_global, regs.global(), GRSTCTL, CSRST: 1);
            while read_reg!(otg_global, regs.global(), GRSTCTL, CSRST) == 1 {} // <- Hanging on this line
            //...

Section 7 of reference manual
image

@David-OConnor David-OConnor added the bug Something isn't working label Nov 4, 2022
@David-OConnor
Copy link
Owner

David-OConnor commented Nov 4, 2022

Oh interesting. Of note, the CRS register block, and enable bit in RCC is supported in most STM32 families. It's supported in this lib on L4, for example. Probably all families that have an HSI48. I haven't ops tested it on L4, but I use it on G4, and it compiles in the PAC. I'm a bit surprised it's part of the OTG package; I have it as an optional-enable flag in the clock config. ie:

// Enable the Clock Recovery System, which improves HSI48 accuracy.
#[cfg(feature = "h7")]
clocks::enable_crs(CrsSyncSrc::OtgHs);
#[cfg(feature = "g4")]
clocks::enable_crs(CrsSyncSrc::Usb);

Of note

@seanybaggins
Copy link
Contributor Author

Taking a closer look at the data sheet, looks like this board does not have a 48 MHz HSI Clock

image

@seanybaggins
Copy link
Contributor Author

Here are the possible clocks that feed into the USB-otg for the STM32L476ZGTx

image

@David-OConnor
Copy link
Owner

David-OConnor commented Nov 4, 2022

So, I think the core is this: The PAC and SVDs are divided by the final number in L4 series. There are a few exceptions re features that use the second. For example, I had to add a new PAC branch for L412 and 422 that use the RTC from newer series. I'm not sure the proper way to handle this. Some combo of changes to PAC and usbotg lib? What do you think?

If you get a chance, could you skin the CRS section of this lib's baseline clock section? I think it blanket allows CRS for all L4, which is evidently not correct. Not a huge deal since you can just choose not to enable if you know yours doesn't have it. Thoughts?

In general, the L4 line is a bit of a pain because diff models have diff versions of periphs! Ie some L4s have the newer periphs for RTC, USB, DMA etc you see on G4/L5/H7, while some have various combos of the old ones.

Sounds like, for example, if you tried to enable CRS via the HAL fn on your board, weird stuff might happen.

@seanybaggins
Copy link
Contributor Author

I'm not sure the proper way to handle this. Some combo of changes to PAC and usbotg lib? What do you think?

I definitely think there should be a change at the PAC. Looking at the memory map and comparing the L476 to the other variants, its clear that the CRS register is not meant to be accessed for the L476 variant.

image
image

Looking at modifying the synopsys-usb-otg to get it working for my board.

If you get a chance, could you skin the CRS section of this lib's baseline clock section? I think it blanket allows CRS for all L4, which is evidently not correct. Not a huge deal since you can just choose not to enable if you know yours doesn't have it. Thoughts?

Definitely in favor of adding some conditional compilation to get rid of the usb::enable_crs as an option for boards that don't support it. My first priority is to get usb working for my board. Once I have that working I will try to circle back on this library and do some polishing.

@David-OConnor
Copy link
Owner

David-OConnor commented Nov 4, 2022

Sounds good. Of note, I caught the RTC thing after swapping what I thought were equivalent MCUs (L432 vice L412), and the RTC broke. Sounds like a similar case here. The fix was a pain because how the PAC broke down L4 variants wasn't compatible with which ones used the new RTC. With that in mind... I'm not sure how to feature gate the enable_crs fn based on how the PAC breaks down variants. Most L4 variants have it. (ie if you check the other L4 RM)

@seanybaggins
Copy link
Contributor Author

So I commented out the following lines in synopsys-usb-otg/src/bus.rs

            // ...
            // Perform core soft-reset
            // while read_reg!(otg_global, regs.global(), GRSTCTL, AHBIDL) == 0 {}
            // modify_reg!(otg_global, regs.global(), GRSTCTL, CSRST: 1);
            // while read_reg!(otg_global, regs.global(), GRSTCTL, CSRST) == 1 {} // <- Hanging on this line
            //...

In my example, I am making it to the poll function call. I still don't see a usb device presented on my PC though. My guess is that I am still getting something wrong in initialization.

@David-OConnor
Copy link
Owner

David-OConnor commented Nov 4, 2022

Hmm. Of note, one thing that can get you is if the MCU doesn't respond to the host very quickly, the host will permanently drop the connection. So the USB poll needs to be on an interrupt (or blocking loop to test), and there can't be any long blocking delays anywhere on the program after it's initialized.

Or, relevant to prev discussion here, if the 48mhz clock isn't set up correctly. My goto is HSI48 with CRS synced to USB, but it sounds like you can't do that.

@seanybaggins
Copy link
Contributor Author

Managed to get this working using a different clock. See #65.

There are compiler warnings within the hal. I thought the example could help chase down and get rid of those warnings.

@David-OConnor
Copy link
Owner

Sweet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants