Search code examples
rustembeddedrp2040

How to correctly share peripherals/GPIO between threads/cores using Rust embedded with embassy on RP2040


I am learning Rust embedded right now and I am trying to implement multi-core processing in my application. I am unsure on what is the right way to share peripherals like GPIO pins between threads/cores.

I am using an Seeduino XIAO RP2040 for this project and embassy-rp as my HAL.

I am using CDC ACM (Serial over USB) in this project and want this to be on its own core. So currently I am running the USB stuff on Core0 (when I tried putting it on Core1 it was not able to register with Windows) and have a dummy task that cycles through RGB-LED colors on Core1.

When moving the LED code to its own task I realized that embassy_rp::init(Default::default()); can only be called once to get an instance of the peripherals.

I then found that I can create a PerhipheralRef to share my needed GPIO pins for the LEDs with my Core1 task. I would have liked to have the task take AnyPin as parameter, but the compiler complained that embassy_rp::PeripheralRef<'static, PIN_17> is different from embassy_rp::PeripheralRef<'static, AnyPin> so I had to specify the PIN numbers i was receiving.

Is there a way to make this code take any pin or is there a different way of having both cores accessing the peripherals?

Thanks in advance.

Following is an excerpt of my code

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
[... imports ...]
bind_interrupts!(struct Irqs {
    USBCTRL_IRQ => InterruptHandler<USB>;
});

static mut CORE1_STACK: Stack<4096> = Stack::new();
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();

#[embassy_executor::task]
async fn core1_task(
    led_r_pin: embassy_rp::PeripheralRef<'static, PIN_17>, <== this is the part I would like to have dynamic
    led_g_pin: embassy_rp::PeripheralRef<'static, PIN_16>,
    led_b_pin: embassy_rp::PeripheralRef<'static, PIN_25>,
) {
    let mut led_r = Output::new(led_r_pin, Level::High);
    let mut led_g = Output::new(led_g_pin, Level::High);
    let mut led_b = Output::new(led_b_pin, Level::High);

    loop {
        // set high turns LED of as the LEDs on the XIAO are hooked up to 3.3V directly
        // 3.3V --- |>|---- Pin
        info!("all off!");
        led_b.set_high();
        led_r.set_high();
        led_g.set_high();
        Timer::after(Duration::from_secs(1)).await;

        info!("blue on!");
        led_b.set_low();
        led_r.set_high();
        led_g.set_high();
        Timer::after(Duration::from_secs(1)).await;
        [... cycle through rgb ...]
    }
}

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_rp::init(Default::default());
    let usb_pin = embassy_rp::Peripheral::into_ref(p.USB);
    let led_r_pin = embassy_rp::Peripheral::into_ref(p.PIN_17);
    let led_g_pin = embassy_rp::Peripheral::into_ref(p.PIN_16);
    let led_b_pin = embassy_rp::Peripheral::into_ref(p.PIN_25);

    embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
        let executor1 = EXECUTOR1.init(Executor::new());
        executor1.run(|spawner| {
            spawner
                .spawn(core1_task(led_r_pin, led_g_pin, led_b_pin))
                .unwrap()
        });
    });

    [... setup usb serial and run an "echo application" ...]
}

[... some usb handling stuff ...]

Solution

  • AnyPin impelments From<T> for every pin (like it's name suggests) so you can just convert them over:

    use embassy_rp::Peripheral as _;
    let led_r_pin = p.PIN_17.into().into_ref();
    //...
    #[embassy_executor::task]
    async fn core1_task(
        led_r_pin: embassy_rp::PeripheralRef<'static, AnyPin>,
        led_g_pin: embassy_rp::PeripheralRef<'static, AnyPin>,
        led_b_pin: embassy_rp::PeripheralRef<'static, AnyPin>,
    ) {
    //...
    }
    

    That should work, but if the compiler has trouble deriving the type for the .into() calls you can help it by turbofish .into::<AnyPin>() or just using the from implementation directly:

    let led_r_pin = AnyPin::from(p.PIN_17).into_ref();