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 Interface Not Working #544

Open
riccardostokker opened this issue Oct 20, 2022 · 3 comments
Open

USB Interface Not Working #544

riccardostokker opened this issue Oct 20, 2022 · 3 comments

Comments

@riccardostokker
Copy link

Hello Everyone!
It's my first post here. I'm trying to learn Rust programming for embedded devices, and I'd like to set up USB communication for my Nucleo F413ZH board.
I've managed to set up everything correctly and I've tested a simple Blink program, which is working. I've tried moving on to USB communication, writing the following two files:

main.rs

#![deny(unsafe_code)]
#![allow(clippy::empty_loop)]
#![no_main]
#![no_std]

mod usb;

// dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`
#[cfg(debug_assertions)]
use panic_halt as _;

// release profile: minimize the binary size of the application
#[cfg(not(debug_assertions))]
use panic_abort as _;

use cortex_m_rt::entry;
use stm32f4xx_hal as hal;
use crate::hal::{pac, prelude::*};

fn initialize_clocks(rcc: hal::rcc::Rcc) -> hal::rcc::Clocks {
    return rcc
        .cfgr
        // We will use the external 8MHz HSE oscillator
        .use_hse(8.MHz())
        // We will run the internal clock at 64MHz
        .sysclk(64.MHz())
        .pclk1((8).MHz())
        .freeze();
}

use stm32f4xx_hal::otg_fs::{USB};

#[entry]
fn main() -> ! {

    let dp = pac::Peripherals::take().unwrap() ;
    let cp = cortex_m::Peripherals::take().unwrap();

    // Initialize clocks
    let clocks = initialize_clocks(dp.RCC.constrain());

    // Initialize USB
    let gpioa = dp.GPIOA.split();
    let usb_configuration = USB {
        usb_global: dp.OTG_FS_GLOBAL,
        usb_device: dp.OTG_FS_DEVICE,
        usb_pwrclk: dp.OTG_FS_PWRCLK,
        pin_dm: gpioa.pa11.into_alternate(),
        pin_dp: gpioa.pa12.into_alternate(),
        hclk: clocks.hclk(),
    };
    usb::initialize_usb(usb_configuration);

    // Create an the LED abstraction. On the Nucleo-401RE it's connected to pin PA5.
    let gpiob = dp.GPIOB.split();
    let mut led = gpiob.pb7.into_push_pull_output();

    // Create a delay abstraction based on SysTick
    let mut delay = cp.SYST.delay(&clocks);

    loop {
        // On for 1s, off for 1s.
        led.set_high();
        delay.delay_ms(1000_u32);
        led.set_low();
        delay.delay_ms(1000_u32);
    }
}

usb.rs

#![allow(unsafe_code)]

use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use stm32f4xx_hal::otg_fs::{UsbBus, USB};
use stm32f4xx_hal::pac::{interrupt, Interrupt};
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::prelude::*;
use usbd_serial::SerialPort;

// Make USB serial device globally available
static G_USB_SERIAL: Mutex<RefCell<Option<SerialPort<UsbBus<USB>>>>> =
    Mutex::new(RefCell::new(None));

// Make USB device globally available
static G_USB_DEVICE: Mutex<RefCell<Option<UsbDevice<UsbBus<USB>>>>> =
    Mutex::new(RefCell::new(None));

pub fn initialize_usb(usb: USB) {
    static mut EP_MEMORY: [u32; 1024] = [0; 1024];
    static mut USB_BUS: Option<UsbBusAllocator<stm32f4xx_hal::otg_fs::UsbBusType>> = None;

    unsafe {
        USB_BUS = Some(stm32f4xx_hal::otg_fs::UsbBusType::new(usb, EP_MEMORY.as_mut()));
        let usb_bus = USB_BUS.as_ref().unwrap();

        cortex_m::interrupt::free(|cs| {
            *G_USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(usb_bus));

            *G_USB_DEVICE.borrow(cs).borrow_mut() = Some(
                UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
                    .manufacturer("Fake company")
                    .product("Serial port")
                    .serial_number("TEST")
                    .device_class(usbd_serial::USB_CLASS_CDC)
                    .build(),
            );
        });

        cortex_m::peripheral::NVIC::unmask(Interrupt::OTG_FS);
    }
}

#[interrupt]
unsafe fn OTG_FS() {
    static mut USB_SERIAL: Option<SerialPort<UsbBus<USB>>> = None;
    static mut USB_DEVICE: Option<UsbDevice<UsbBus<USB>>> = None;

    let usb_dev = USB_DEVICE.get_or_insert_with(|| {
        cortex_m::interrupt::free(|cs| {
            // Move USB device here, leaving a None in its place
            G_USB_DEVICE.borrow(cs).replace(None).unwrap()
        })
    });

    let serial = USB_SERIAL.get_or_insert_with(|| {
        cortex_m::interrupt::free(|cs| {
            // Move USB serial device here, leaving a None in its place
            G_USB_SERIAL.borrow(cs).replace(None).unwrap()
        })
    });

    if usb_dev.poll(&mut [serial]) {
        let mut buf = [0u8; 64];

        match serial.read(&mut buf) {
            Ok(count) if count > 0 => {
                // Echo back in upper case
                for c in buf[0..count].iter_mut() {
                    if 0x61 <= *c && *c <= 0x7a {
                        *c &= !0x20;
                    }
                }

                let mut write_offset = 0;
                while write_offset < count {
                    match serial.write(&buf[write_offset..count]) {
                        Ok(len) if len > 0 => {
                            write_offset += len;
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
    }
}

However, whenever I run lsusb in my terminal, after connecting the board to my PC, nothing shows up. I should be able to hear a notification and see a Fake Company Serial Port usb device.
Why is that (not) happening?

Thank you very much,
Riccardo

@burrbull
Copy link
Member

cc @Disasm

@Disasm
Copy link
Member

Disasm commented Oct 21, 2022

I'm not sure if USB interrupts work at all with this chip. And there is a bug for a different one: stm32-rs/synopsys-usb-otg#13
Polling in main loop should still work, though.

@hacknus
Copy link
Contributor

hacknus commented Sep 8, 2023

I have a similar issue with a STM32f405. The interrupts fire when a windows or macOS machine is on the host side, but when I connect it to a linux machine (ubuntu), the interrupts never fire and I have to poll in the main loop... Does anybody have an idea on where the problem could lie?

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