Controlled line change

In this part, we will start using a periodic ticker to run some tasks at designated times. For example, we want to display frames at a pace of 80 FPS (frames per second) as it is most pleasant for the eyes to not have frequencies below 70Hz. Since each line of the matrix should get the same display time, we will call a display task 80×8=640 times per second. This display task will display the next line.

Blinking led

In order to check that you do not block the system, you want to create a new asynchronous task which will make the green led blink.

❎ Comment out your matrix display loop. You will reenable it later.

❎ Create a new task blinker as an asynchronous function with attribute embassy_executor::task. This function:

  • receives the green led port (PB14) as an argument
  • initialize the port as an output
  • loops infinitely while displaying this pattern:
    • three quick green flashes
    • a longer pause

Don't forget that you can use asynchronous functions from Timer as you did just before.

❎ Using the Spawner object passed to your main program, spawn the blinker task.

❎ Check that the green led displays the expected pattern repeatidly.

❎ Reenable the matrix display loop (after you have spawned the new task).

You should no longer see your green led blink: your matrix display loops never returns and never suspends itself as an asynchronous task would do while waiting for the time to switch to the next line. We will take care of that.

Controlled pace

We want to make an asynchronous task whose job is to take care of displaying the lines of the led matrix at the right pace in order to get a 80Hz smooth display. For this we will need to build the elements:

  • An asynchronous task that will be spawned from main()
  • A Matrix instance to give to this task – we already have it!
  • A Ticker object to maintain a steady 80Hz rate.
  • A way to be able to modify the Image displayed on the matrix from other tasks, such as main(). We will need to use a Mutex from the crate embassy-sync to protect the Image being accessed from several places.

Let's build this incrementally.

Asynchronous display task

❎ Make a new display asynchronous task taking a Matrix instance as an argument, and copy the current display loop inside. Put an infinite loop around, as we do not want to leave the display task, ever! Add what is needed to make it working (such as a static Image). Spawn the display task from main().

Note that you have to supply a lifetime, as your Matrix type gets one. Fortunately, 'static will work, as this is the lifetime of the ports you configured from your Peripherals object.

Check that your program still works. Still, no green led blinking yet. Both the blinker and display asynchronous tasks run on the same executor, but the display task never relinquishes control to the executor.

Ticking

❎ In your display task, create a Ticker object which will tick every time it should display a new line. 8 lines, 80 Hz, that gives? You got it! Don't hesitate to use the convenience methods such as Duration::from_hz().

You now want Matrix::display_image() to use this ticker.

❎ Add a ticker parameter to display_image(). You just want to use it, not take ownership of it, so you need a reference. Since you note that the ticker's next() method requires a &mut self, you need to receive the ticker as a mutable reference as well.

❎ Make display_image() an asynchronous function, since it needs to wait for the ticker to tick.

❎ In display_image(), wait until the tickers tick before displaying a row, so that rows are evenly spaced every 1/640th of a second.

❎ In display(), pass a mutable reference to the ticker to display_image().

If everything goes well, you should see both the image on your led matrix and the green led pattern. Neat, eh?