Moving value "into" async task

What I am trying to achieve is this:

I have a display driver type that uses an SPI and a few Output pins (of a STM32 controller). This type is tested and works as it should. The code to initialize and use it looks as follows:

        let mut spi_config = embassy_stm32::spi::Config::default();
        spi_config.frequency = mhz(1);

        let mut lcd;
        let lcd_spi = Spi::new_txonly(
            perip.PA5,  // SCK
            perip.PA7,  // MOSI
            NoDma,      // TxDma
            NoDma,      // RxDma

        let lcd_cs = Output::new(perip.PA4, Level::High, Speed::VeryHigh);
        let lcd_rs = Output::new(perip.PA6, Level::High, Speed::VeryHigh);
        let lcd_reset = Output::new(perip.PB10, Level::High, Speed::VeryHigh);
        let lcd_backlight = Output::new(perip.PB11, Level::Low, Speed::Low);
        lcd = Lcd::new(lcd_spi, lcd_cs, lcd_rs, lcd_reset, lcd_backlight);
        // ... lcd usable here

Everything works & happiness so far :slight_smile:

But now my problems begin.
I would like to move that value into a task that handles the display, but this does not work:

async fn lcd_task(lcd: Display) {
    lcd.strout(b"Hello\nworld !").await;
    loop {

async fn main(spawner: Spawner) {
    // ....
         let lcd = ...;
    // ....

The error I get is implicit elided lifetime not allowed here with "here" referring to the Display argument to the async task.

  • It all looks like the task gets a reference to lcd, but why can't I move the value into the async task, deallocating it from the stack in main ?
  • Is that impossible because async tasks use the same stack? But then, in principle, they could be in different executors, couldn't they?

The other question I have is:
My Display type is defined as follows:

pub struct Lcd<SPI, O1, O2, O3, O4> {
    spi: SPI,
    cs: O1,
    rs: O2,
    reset: O3,
    backlight: O4,
    // ....
  • having the Lcd type being generic over 4 different output pin types seems rather complicated with respect to the type signature I think I need. Is there an obvious way to avoid this that I don't see?

And in I then have, to match the type:

type LcdSpi<'a> = Spi<'a, SPI1, NoDma, NoDma>;
type LcdCs = Output<'static, PA4>;
type LcdRs = Output<'static, PA6>;
type LcdMosi = Output<'static, PA7>;
type LcdClk = Output<'static, PA5>;
type LcdReset = Output<'static, PB10>;
type LcdBacklight = Output<'static, PB11>;

type Display<'a> = Lcd<LcdSpi<'a>, LcdCs, LcdRs, LcdReset, LcdBacklight>;
  • this seems to work but is rather awkward requiring changes at different places when I, for example would use another pin as CS.

What's the definition of Display, then? It looks like it is actually Display<'some_lifetime_parameter>.

upd: answered too hastily, sorry - now I see that this is exactly the case. You can't move into the async task anything that contains any non-'static references (since these references might dangle), and Display (more specifically, LcdSpi) is declared as containing (maybe not literally, but semantically, via some raw pointer) some reference.

You have to define your function like this:
async fn lcd_task(lcd: Display<'static>)
And then you have to find a way to make your Spi 'static. I think this is possible in main with embassy, but I don't know how from memory.
Worst case would be to transmute it to 'static using unsafe. This would be ok as long as your Spi never goes out of scope.

Update: I just looked into it more. It is quite possible that your Spi already is 'static.

Thanks, now everything seems to work.

The first change I made is add a static lifetime for the Spi:

type LcdSpi<'a> = Spi<'static, SPI1, NoDma, NoDma>;
type Display<'a> = Lcd<LcdSpi<'static>, LcdCs, LcdRs, LcdReset, LcdBacklight>;

Then, I needed to make the argument to the async task mutable.

async fn lcd_task(mut lcd: Display<'_>) {

Those <'a> type parameters aren't doing anything now and can be removed.

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.