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 ...]
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();