Create lv_meter on two different displays, center knob is missing


I’m writing display drivers for use with Mbed-OS. First display ILI9341 works, 2nd display GCA01 (circular TFT) also.
I have used the lv_meter example, and modified that I can pass the disp_t as argument for screen creation. This works in general, only the center knob is missing on the 2nd created display. Is there any global function? It looks like the knob is only drawn on the active display.

Another problem is the different color type. Both displays are using 16 bit RGB565, but the GC9A01 needs the LV_COLOR_16_SWAP set to 1. I’ve read that the displays must use the same color format, is there an option to swap the RGB per display? I haven’t checked the display possibilities yet, maybe the RGB can be swapped by the TFT controller?

What MCU/Processor/Board and compiler are you using?

STM32F407VE with ILI9341 and GC9A01 multidisplay
Mbed-OS 6.11, gcc 9

What LVGL version are you using?

8.0 HEAD

What do you want to achieve?

testing drivers, using multiple displays

What have you tried so far?

  • changed order of driver init, the meter center knob is always drawn on the first display
  • changed order of screen creation, no effect

Code to reproduce

add two displays, code for lv_meter is basically from example, with extension for passing the display as root:

// in main:
    lv_gaugescreen_param_t p2 = {0};
    lv_gauge_screen(lvglDisplayGC9A01_1->getLVDisp(), &p2);

    lv_gaugescreen_param_t p1 = {0};
    lv_gauge_screen(lvglDisplay->getLVDisp(), &p1);

void lv_gauge_screen(lv_disp_t* disp, lv_gaugescreen_param_t* retparam)
    lv_obj_t * scr = lv_disp_get_scr_act(disp);

    lv_obj_t* meter = lv_meter_create(scr);
    lv_obj_set_size(meter, 230, 230);

    /*Add a scale first*/
    lv_meter_scale_t * scale = lv_meter_add_scale(meter);
    lv_meter_set_scale_ticks(meter, scale, 41, 2, 10, lv_palette_main(LV_PALETTE_GREY));
    lv_meter_set_scale_major_ticks(meter, scale, 8, 4, 15, lv_color_black(), 10);

    /*Add a blue arc to the start*/
    lv_meter_indicator_t * indic;
    indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_BLUE), 0);
    lv_meter_set_indicator_start_value(meter, indic, 0);
    lv_meter_set_indicator_end_value(meter, indic, 20);

    /*Make the tick lines blue at the start of the scale*/
    indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_BLUE), false, 0);
    lv_meter_set_indicator_start_value(meter, indic, 0);
    lv_meter_set_indicator_end_value(meter, indic, 20);

    /*Add a red arc to the end*/
    indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_RED), 0);
    lv_meter_set_indicator_start_value(meter, indic, 80);
    lv_meter_set_indicator_end_value(meter, indic, 100);

    /*Make the tick lines red at the end of the scale*/
    indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_RED), false, 0);
    lv_meter_set_indicator_start_value(meter, indic, 80);
    lv_meter_set_indicator_end_value(meter, indic, 100);

    /*Add a needle line indicator*/
    indic = lv_meter_add_needle_line(meter, scale, 4, lv_palette_main(LV_PALETTE_GREY), -10);

    retparam->meter = meter;
    retparam->indic = indic;

Screenshot and/or video

I’ve tried to trace the problem in the debugger. On the second display, the size for the center knob is zero. This size is read from style LV_PART_INDICATOR:

It looks like this style is overwritten accidently by some function, I do not find that this style is changed by intention in the code.

Its more complicated. The styles are assigned to the objects, but it looks like the event for updating is using some wrong object. The style for LV_PART_INDICATOR is not overwritten, the style is not found and the default 0 is used for width/height.

what could be necessary for a multidisplay configuration?

(cc @kisvegabor)

I suspect the issue is here: lvgl/lv_hal_disp.c at b56e60acef2cb62994d4561e35f87f784432c4c9 · lvgl/lvgl · GitHub

The default theme is only initialized once, which means that it will only be initialized on the first display. This is probably an oversight. As a workaround, you will have to initialize the theme again yourself after registering the second display:

lv_theme_t * th = lv_theme_default_init(display,  /*Use the DPI, size, etc from this display*/ 
                                        LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN,   /*Primary and secondary palette*/
                                        false,    /*Light or dark mode*/ 
                                        &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/
lv_disp_set_theme(display, th); /*Assign the theme to the display*/

@embeddedt Thanks, thats it!
As a quick hack, I’ve removed the

 if(lv_theme_default_is_inited() == false) {

and now the disp->theme is set correctly. Of course, now the theme init is done twice, but in lv_theme_default.c is already a check for a previous init, so at least there shouldn’t be memory wasted.

A better solution is maybe a getter for the themes that handles the first time initialization.

But I see, multiple display handling is more demanding. The same themes for different display sizes maybe also different, e.g. the padding depends on the display size.

A solution could be to have a function like lv_theme_default_init_new() that allocates a new lv_theme_t and lv_style_ts internally, initializes them with the new colors, fonts and DPI, and returns a pointer to the new theme. It seems very flexible and doable without API break.

The styles are already collected in my_theme_styles_t so new styles can be easily allocated.

What do you think?

thanks, I will check this, I haven’t used lvgl except for some tests with v6.
I have used now the suggested solution by embeddedt and execute the disp->theme settings after the driver registration again. There I could use also a custom theme, that sounds fine.

colors for the GC9A01 could be fixed by switch between 8 bit and 16 bit SPI in the flush routine.
8 bit for sending the set_addr_win, 16 bit for sending the pixel data.