Image change
Right now, the display tasks does more than displaying something, as it takes care of the Image itself. It should only access it when needed, but creating and modifying the image should not be its responsibility. Let's fix that.
Sharing a Image between tasks
We will create a shared Image, protected by a mutex. However, you have to understand how Embassy's mutexes work first.
Embassy asynchronous mutexes
Embassy's mutexes cannot use spin locks, as spin locks loop forever until they get the lock. If Embassy did this, it would block the current asynchronous task, and thus the whole executor.
Embassy's mutexes are asynchronous-friendly, and will yield when they cannot lock the resource immediately. However, to implement it, Embassy still needs a real mutex (which Embassy calls a "raw mutex", or "blocking mutex") for a very short critical section.
Since all our tasks are running on the same executor, they will never try to lock the raw mutex at the same time. It means that we can safely use the ThreadModeRawMutex as raw mutex.
Creating the shared image object
So we want to create a global (static) Image object protected by a Mutex using internally a ThreadModeRawMutex.
β Import embassy_sync::mutex::Mutex and embassy_sync::blocking_mutex::raw::ThreadModeRawMutex.
β Declare a new global (static) IMAGE object of type Mutex<ThreadModeRawMutex, Image> and initialize itβ¦ but with what?
Creating the initial image
Initialization of static variables are done before any code starts to execute. The compiler must know what data to put in the global IMAGE object.
We could try to use:
static IMAGE: Mutex<ThreadModeRawMutex, Image> = Mutex::new(Image::new_solid(Color::GREEN));
but the compiler will complain:
error[E0015]: cannot call non-const fn `tp_led_matrix::Image::new_solid` in statics
|
| static IMAGE: Mutex<ThreadModeRawMutex, Image> = Mutex::new(Image::new_solid(Color::GREEN));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Indeed, it cannot execute the call to Image::new_solid() before even code starts to execute. However, there is an easy solution here! π‘
The code of Image::new_solid() is likely simple (if it is not, fix it):
impl Image {
pub fn new_solid(color: Color) -> Self {
Image([color; 64])
}
}
Indeed, this is so simple that this could be done at compilation time if the function were a const one. const functions, when given constant parameters, can be replaced by their result at compilation time.
By adding the const keyword:
impl Image {
pub const fn new_solid(color: Color) -> Self {
Image([color; 64])
}
}
the compiler will now be able to create the data structure for the mutex containing the image with the green constant at compilation time, and place it the .data section.
Putting it together
β Add the const keyword to the Image::new_solid() function and initialize the IMAGE object. You may want to add a new constant Color, such as BLACK, even though it may be useful at the beginning to look at a visible image.
β Modify the display task so that, before being displayed, each image is copied locally in order not to keep the mutex locked for a long time.
Changing images dynamically
β Modify the main task so that the IMAGE object is modified, every second or so, by another one.
Don't make things complicated. You should noticed that your display changes every second, while being pleasant to look at. The green led should blink its pattern at the same time.
This is starting to look nice.