Dedicated executor

Until now, we used only one executor in thread mode (the regular mode in which the processor runs, as opposed to interrupt mode). It means that Embassy's executor will execute one asynchronous task until it yields, then the other, then the other, and so on. If for any reason one task requires a bit more time than expected, you might delay other tasks such as the display task. In this case, you might notice a short glitch on the display.

To prevent this, we will use a dedicated interrupt executor to run our display task. In this scenario, when it is time to display a new line on the display, an interrupt will be raised and the executor will resume the display task while still in interrupt mode, interrupting the rest of the program.

You will have to choose an unused hardware interrupt, and:

  • configure it to the priority you want to use, with regard to other interrupts in the system
  • start the executor, telling it to tell its tasks to raise this interrupt by software (pend the interrupt, as in make it pending) when they have progress to signal
  • call the executor's on_interrupt() method in the ISR, so that the executor knows that it must poll its tasks

Those are three easy tasks. We will choose interrupt UART4, and set it to priority level Priority::P6:

❎ Add the executor-interrupt feature to the embassy-executor dependency in Cargo.toml.

❎ Create a static DISPLAY_EXECUTOR global variable, with type InterruptExecutor.

❎ Choose an unused interrupt (pick UART4), configure it with an arbitrary priority (use Priority::P6). Start the DISPLAY_EXECUTOR and associate it with this interrupt. Use the returned spawner to spawn the display task.

❎ Write an ISR for this interrupt, and redirect the event to the executor:

#[interrupt]
unsafe fn UART4() {
    DISPLAY_EXECUTOR.on_interrupt();
}

Note that ISR are unsafe functions, as doing the wrong thing in an interrupt routine might lock up the system.

At this stage, you might notice that your code does not compile: the NEXT_IMAGE data structure uses a ThreadModeRawMutex as its internal mutex. Such a mutex, as its name indicates, can only be used to synchronize tasks running in thread mode, not in interrupt mode.

❎ Use a CriticalSectionRawMutex as an internal mutex for NEXT_IMAGE, because such a mutex is usable to synchronize code running in interrupt mode with code running in thread mode.

Your display should now be as beautiful as ever.