STM32 Embassy using peripherals in tasks

Hello!
I'm trying to learn rust for embed. I'm struggle with using multiple tasks that uses peripheral.

I can't understand how to share embassy_stm32::Peripherals between tasks.

For example embassy_stm32::Peripherals::take is private. cortex_m::Peripherals::take().unwrap() do not have PA6 or other pins.

I can't send spi to spawner like spawner.spawn(gyro_task(spi)).unwrap(); as task can't be generic and I don't want to have long code for task arguments and want some flexibility.

Usage of something like

static mut PERIPHERALS:Peripherals=embassy_stm32::init(Config::default());
// another task
let mut led = Output::new(PERIPHERALS.PA4, Level::Low, Speed::Low);

will produce error
move occurs because PERIPHERALS.PA4has typePA4, which does not implement the 'Copy' trait
How to share SPI if, for example I have multiple devices on one bus?
I understand how to do such task in C with ease. But what is proper way in rust+embassy if you don't want your code to be hardcoded for SPI1 PA3 etc?

this is an unfortunate limitation of the #[embassy_executor::task] macro. the usual workaround is to use generic in the common "driver" code, and then write each individual tasks entry point as a simple wrapper. something like this:

// this is generic, use whatever trait bounds to fit your needs
// I use `spi::Instance` as an example
async fn my_spi_driver<Spi: embassy_stm32::spi::Instance>(spi: SPI) {
    //...
}

// tasks are not generic, must use concrete type like SPI1, SPI3
// these are application specific anyway

#[task]
async spi_task1(spi: SPI1) {
    // ... app specific code here
    // call the generic driver
    my_spi_driver(spi).await
}

#[task]
async spi_task2(spi: SPI3) {
    //...
    my_spi_driver(spi).await
}

you need to synchronize the access to the shared resource (Spi in this case), just like any concurrent problem, basically, you have two options: message passing and locking.

with message passing, it goes like this: create a dedicated task for the Spi driver, which owns the specific spi bus. drivers for each device on the bus can only talk to the bus driver, e.g. by sending commands to a channel.

if you prefer accessing the spi controller directly in the device driver code, you can use mutex.

see embassy_sync for details, there are channels, mutexes, and more.

2 Likes

Thank you! Your idea and upgrate of embassy versions worked it out!