Cannot pass peripheral to embassy async task and use it there

I am trying to build an embassy-based application based on the usb_serial.rs example, for an STM32F103C8.
The problem is that I really struggle with passing peripherals to async fns and lifetime definitions (I am new to Rust):

Here is a stripped-down small example that shows the error:

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use defmt::{*};
use embassy_executor::Spawner;
use embassy_stm32::gpio::{AnyPin, Level, Output, Speed, Pin};
use embassy_stm32::time::Hertz;
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
use embassy_stm32::usb::{Driver};

pub struct Board {
    pub led: Output<'static, AnyPin>,
    pub dplus: Output<'static, AnyPin>,
    pub dminus: Output<'static, AnyPin>,
    pub usb: embassy_stm32::peripherals::USB,
}

impl Board {
    pub fn init(p: embassy_stm32::Peripherals) -> Board {
        let led = Output::new(p.PC13.degrade(), Level::High, Speed::Low);
        let dplus = Output::new(p.PA12.degrade(), Level::Low, Speed::Low);
        let dminus = Output::new(p.PA11.degrade(), Level::Low, Speed::Low);

        Board {
            led,
            dplus,
            dminus,
            usb: p.USB,
        }
    }
}

// ---------------------------- usb task -----------------------------

bind_interrupts!(struct Irqs {
    USB_LP_CAN1_RX0 => usb::InterruptHandler<peripherals::USB>;
});

#[embassy_executor::task]
pub async fn usb_task(
    mut dp: Output<'static, AnyPin>,
    mut dm: Output<'static, AnyPin>,
    mut usb: embassy_stm32::peripherals::USB,
) {
    {
        // BluePill board has a pull-up resistor on the D+ line.
        // Pull the D+ pin down to send a RESET condition to the USB bus.
        // This forced reset is needed only for development, without it host
        // will not reset your device when you upload new firmware.
        dp.set_low();
        Timer::after_millis(10).await;
    }
    // ********** this line causes an error **********
    let driver = Driver::new(usb, Irqs, dp, dm);
}

// --------------------------- blink task ----------------------------

#[embassy_executor::task]
async fn blink(mut led: Output<'static, AnyPin>) {
    loop {
        led.set_low();
        Timer::after_millis(300).await;
        led.set_high();
        Timer::after_millis(700).await;
        info!("tick!");
    }
}

// ------------------------------ main -------------------------------

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let mut config = Config::default();
    config.rcc.hse = Some(Hertz(8_000_000));
    config.rcc.sys_ck = Some(Hertz(48_000_000));
    config.rcc.pclk1 = Some(Hertz(24_000_000));

    let perip = embassy_stm32::init(config);

    let board = Board::init(perip);

    spawner.spawn(blink(board.led)).unwrap(); // this works

    // *************
    spawner.spawn(usb_task(&mut board.dplus, &mut board.dminus, board.usb)).unwrap();

    loop {
        // for the moment, don't do anything here....
        Timer::after_millis(10000).await;
        info!("tack!");
    }
}

What I try to do is first collect the peripherals in struct Board, and then pass them to async tasks. For the LED blink task, this works. However, for the usb task, I ran into problems with the call of Driver::new.

I get

error[E0277]: the trait bound `Output<'_, AnyPin>: DerefMut` is not satisfied
   --> src/main.rs:85:41
    |
85  |     let driver = Driver::new(usb, Irqs, dp, dm);
    |                  -----------            ^^ the trait `DerefMut` is not implemented for `Output<'_, AnyPin>`
    |                  |
    |                  required by a bound introduced by this call
    |
    = help: the following other types implement trait `Peripheral`:
              NoDma
              AnyPin
              ADC1
              ADC2
              AFIO
              BKP
              CAN
              embassy_stm32::peripherals::CRC
            and 126 others
    = note: required for `Output<'_, AnyPin>` to implement `Peripheral`
note: required by a bound in `embassy_stm32::usb::Driver::<'d, T>::new`
   --> /data/_programming/STM32/rust/embassy/embassy-stm32/src/usb/usb.rs:257:18
    |
254 |     pub fn new(
    |            --- required by a bound in this associated function
...
257 |         dp: impl Peripheral<P = impl DpPin<T>> + 'd,
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Driver::<'d, T>::new`

I tried to declare the pins as mutable reference, but this did not help.
How can I solve the requirements for the call of Driver::new?

It seems I am going in circles for hours without understand the core of the problem... I would highly appreciate an explanation of what exactly happens here.

I am not that familiar with embassy_stm32, but I think your problem is that you initialize the USB pins. I think you need to pass the raw pins PA12 and PA11 instead of the initialized Output pins. I suppose the driver will initialize them appropriately.

For context: The Output pins do not implement Peripheral while the raw pins do and this is what the Driver expects.

as @Karsten pointed out, you need pass the peripheral PA12 and PA11 to the driver, don't configure it as gpip::Output, the usb driver demand ownership of these peripherals. try to change the usb_task() parameter types (and also the Board struct).

 pub struct Board {
     pub led: Output<'static, AnyPin>,
-    pub dplus: Output<'static, AnyPin>,
-    pub dminus: Output<'static, AnyPin>,
+    pub dplus: PA12,
+    pub dminus: PA11,
     pub usb: embassy_stm32::peripherals::USB,
 }

 pub async fn usb_task(
-    mut dp: Output<'static, AnyPin>,
-    mut dm: Output<'static, AnyPin>,
+    mut dp: PA12,
+    mut dm: PA11,
     mut usb: embassy_stm32::peripherals::USB,
 ) {
     //...
 }

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.