Here is an example written for MicroPython using SDL2 as the display…
This function gets called from the flush callback. this is what handles passing setting the buffer data to be written and unlocks the mutex so the thread is able to write the data to the display.
mp_lcd_err_t sdl_tx_color(mp_obj_t obj, int lcd_cmd, void *color, size_t color_size, int x_start, int y_start, int x_end, int y_end)
{
LCD_UNUSED(x_start);
LCD_UNUSED(y_start);
LCD_UNUSED(x_end);
LCD_UNUSED(y_end);
LCD_UNUSED(color_size);
mp_printf(&mp_plat_print, "sdl_tx_color started\n");
mp_lcd_sdl_bus_obj_t *self = MP_OBJ_TO_PTR(obj);
while (!self->trans_done) {}
self->trans_done = false;
self->panel_io_config.buf_to_flush = color;
SDL_UnlockMutex(self->panel_io_config.mutex);
if (self->callback != mp_const_none && mp_obj_is_callable(self->callback)) {
mp_call_function_n_kw(self->callback, 0, 0, NULL);
}
mp_printf(&mp_plat_print, "sdl_tx_color finished\n");
return LCD_OK;
}
This is the code the tread runs.
int flush_thread(void *self_in) {
mp_printf(&mp_plat_print, "flush_thread running\n");
mp_lcd_sdl_bus_obj_t *self = (mp_lcd_sdl_bus_obj_t *)self_in;
void* buf;
int pitch;
while (!self->panel_io_config.exit_thread) {
SDL_LockMutex(self->panel_io_config.mutex);
mp_printf(&mp_plat_print, "flush_thread started\n");
if (self->panel_io_config.buf_to_flush != NULL) {
pitch = self->panel_io_config.width * self->panel_io_config.bytes_per_pixel;
buf = self->panel_io_config.buf_to_flush;
SDL_UpdateTexture(self->texture, NULL, buf, pitch);
SDL_RenderClear(self->renderer);
SDL_RenderCopy(self->renderer, self->texture, NULL, NULL);
SDL_RenderPresent(self->renderer);
}
self->trans_done = true;
mp_printf(&mp_plat_print, "flush_thread finished\n");
}
mp_printf(&mp_plat_print, "flush_thread ended\n");
return 0;
}
When the display is initialized before creating the the thread the mutex gets created and then it gets locked. This way when the thread is created and tries to lock the mutex the thread gets stalled. This keeps the thread from consuming all of the CPU time for that core.
I added a second guard for writing the display buffer data which can be seen with this code self->trans_done = true;
. That second guard is used because of needing to call a python function in order to call lv_display_flush_ready
. In MicroPython I am only able to call a python function from C code from the main thread. If you look at the first code example you will see a stall until trans_done
gets set to true
. once that happens I am able to set the pointer to the buffer, release the mutex and then make the callback telling LVGL the buffer is done transferring even tho it really isn’t. In you code you would not have to do this and you can call lv_display_flush_ready
directly from the thread that is flushing the buffer. You are able to do this because there is no allocation that is happening.