Hello, I am greatly struggling to set up LVGL on a STM32H7+LTDC > RGB > 240x320 TFT-LCD setup.
The library seems set-up, but animations look awful. The following short shows the problem: https://www.youtube.com/shorts/l1SwMOpma10
This looks like worse than very bad tearing, it looks extremely buggy, but I don’t doubt this is an issue with my setup.
I am outputting via LTDC to a ST7789V, which then drives the display. Ideally I would bypass the ST7789V RAM and output to the screen directly (via the shift register option), but I can’t seem to get that option to work.
Therefore there’s 2 potential sources of tearing, the “usual” one of drawing to a buffer that’s being read, and the sync difference between the RGB writing to the display driver RAM, and the driver writing its RAM to the LCD. I don’t know how to get rid of the latter.
As a reference, I tested the following:
LTDC->SRCR |= 2; // Vertical blanking reload:shadow registers are reloaded during vblank (prevents tearing)
int colorCounter = 0;
while (true) {
uint16_t* currentFrame = colorCounter % 2 == 0 ? lcdFrame1Origin : lcdFrame2Origin;
LTDC_Layer1->CFBAR = (uint32_t)(colorCounter % 2 == 1 ? lcdFrame1Origin : lcdFrame2Origin);
HAL_Delay(200);
std::fill(currentFrame, currentFrame+240*320, (colorCounter%3)==0 ? 0b11111'000000'00000 : (colorCounter%3)==1? 0b00000'111111'00000 : 0b00000'000000'11111);
colorCounter++;
}
Here I’m using 2 buffers and swapping to the new one after writing to it, at VSync.
When doing that only the aforementionned driver source of tearing remains, as can be seen here: https://www.youtube.com/shorts/ocmkwgcTIfQ
There is some tearing (diagonal, as a result of writing to the driver line-first and it reading line-first…). But as can be seen in the video, it is ‘usual’ tearing, one line accross the screen - nothing that would cause what can be seen with the buttons.
So it must mean I am not doing the double buffering properly with LVGL. (same results are observed on single buffer with LVGL)
This is what I do:
void my_flush_cb(lv_display_t * disp, const lv_area_t * area, unsigned char* color_p) {
UART7_Transmit(std::to_string((uint32_t)(color_p))+"\n");
LTDC_Layer1->CFBAR = (uint32_t)color_p;
lv_display_flush_ready(disp); // Empty flush callback. We dispatch via LTDC so the buffer is always available
}
void lv_example_button_1(void)
{
lv_obj_t * label;
lv_obj_t * btn1 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);
lv_obj_remove_flag(btn1, LV_OBJ_FLAG_PRESS_LOCK);
lv_obj_set_size(btn1, 160, 80);
label = lv_label_create(btn1);
lv_label_set_text(label, "Button");
lv_obj_center(label);
lv_obj_t * btn2 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn2, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 40);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
//lv_obj_set_height(btn2, LV_SIZE_CONTENT);
lv_obj_set_size(btn2, 160, 80);
label = lv_label_create(btn2);
lv_label_set_text(label, "Toggle");
lv_obj_center(label);
}
[In main]
LTDC->SRCR |= 2; // Vertical blanking reload:shadow registers are reloaded during vblank (prevents tearing)
lv_init();
lv_display_t * disp = lv_display_create(320, 240); /*Basic initialization with horizontal and vertical resolution in pixels*/
lv_display_set_flush_cb(disp, my_flush_cb); /*Set a flush callback to draw to the display*/
lv_display_set_buffers(disp, lcdFrame1Origin, lcdFrame2Origin, lcdFrameByteLength, LV_DISPLAY_RENDER_MODE_DIRECT); /*Set an initialized buffer*/
// Change the active screen's background color
lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);
lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0xffffff), LV_PART_MAIN);
lv_example_button_1();
while (true)
{
lv_timer_handler();
HAL_Delay(5);
}
Starting code & example taken from Display interface — LVGL documentation
In flush_cb I set LTDC buffer to reset to the color_p provided by the callback, which should be the full buffer given the selection of direct mode, then call the flush ready func. The change of buffer will happen at next VSync, which could be an issue, but I also don’t expect LVGL to call the flush_cb so soon given LV_DEF_REFR_PERIOD is 33 (default) in lv_conf.
I’m out of ideas here, I’ve been fighting with this LCD for a week and any help would be greatly appreciated.
And while this isn’t LVGL related, if anyone might know what prerequisites there would be for using the “Shift register” aka without RAM mode on ST7xxx / ILIxxxx drivers so I can get rid of the tearing introduced by the driver, I’ll gladly listen. When I turn it on, regardless of row-column swapping or other thing that may make the signal “not match” the LCD, the screen just stops updating, whereas things work just fine when it comes to writing the RGB signal to RAM and letting the driver write its RAM to the display. Thanks.