Only last two screens are displayed

Description

When creating several screens and loading them one after another, only the last two screens are displayed.

MCU and board type

STM32F205RET (128K SRAM, 512K Flash) in a custom board, with gcc-arm(1), GNU make(1), and STM32 HAL.

What LVGL version are you using?

7.11.0

What do you want to achieve?

I want to create several screens and switch between them with no user interaction, instead according to a time delay.

What have you tried so far?

I copied the sample code from the screens doc page.

Code to reproduce

int main(void)
{
  // Initialisation and configuration code
  // ...

  // Screen 1 (never displayed!)
  lv_obj_t *pScreen1 = lv_scr_act();
  lv_obj_t *pLabel1 = lv_label_create(pScreen1, NULL);
  lv_label_set_text(pLabel1, "Screen 1!");
  lv_scr_load_anim(pScreen1, LV_SCR_LOAD_ANIM_MOVE_TOP, 250, 1000, true);

  // Screen 2 (never displayed!)
  lv_obj_t *pScreen2 = lv_obj_create(NULL, NULL);
  lv_obj_t *pLabel2 = lv_label_create(pScreen2, NULL);
  lv_label_set_text(pLabel2, "Screen 2!");
  lv_scr_load_anim(pScreen2, LV_SCR_LOAD_ANIM_MOVE_TOP, 250, 1000, true);

  // Screen 3 (display jumps to Screen3 at program start?)
  lv_obj_t *pScreen3 = lv_obj_create(NULL, NULL);
  lv_obj_t *pLabel3 = lv_label_create(pScreen3, NULL);
  lv_label_set_text(pLabel3, "Screen 3!");
  lv_scr_load_anim(pScreen3, LV_SCR_LOAD_ANIM_MOVE_TOP, 250, 1000, true);

  // Screen 4 (screen load and animated transition, working properly.)
  lv_obj_t *pScreen4 = lv_obj_create(NULL, NULL);
  lv_obj_t *pLabel4 = lv_label_create(pScreen4, NULL);
  lv_label_set_text(pLabel4, "Screen 4!");
  lv_scr_load_anim(pScreen4, LV_SCR_LOAD_ANIM_MOVE_TOP, 250, 1000, true);

  while(1)
  {
    lv_task_handler();
    HAL_Delay(5);
  }
}

Suspicions

  • Is my MCU’s 128K SRAM too little for storing all four screens?
  • Is this problem why some people create custom heaps?

Video capture

Screen animation problem 1

It might help to think of LVGL API calls as pushing tasks to a queue which are then executed when lv_task_handler runs. What’s happened here is that you’ve queued four transitions to execute simultaneously, which is obviously not your intent.

To solve this, you would need to run only one of the lv_scr_load_anim calls initially, and then use some type of asynchronous callback (like an LVGL task) to run the other one 1 second later.

That’s a clear explanation why the four screen load calls are simultaneous @embeddedt, thanks a lot.

I can restructure most of the program flow to be more event based and screens load according to button presses.

Nevertheless, a splash screen is by definition noninteractive. So I’m not sure how to implement one without using a event based button callback.

You can still implement a splash screen; you just need to use an LVGL task like I stated to implement a delay before changing to the main screen.

After an experiment, it seems I can do a splash screen without using lv_task_once(3) or lv_async_call(3) @embeddedt .

The LVGL lack of task synchronisation doesn’t affect programs starting with less than three automatic (no user interaction) screen calls, so this works:

int main(void)
{
    // Initialise and configure
    ...
    lv_img_create(lv_scr_act(), NULL);  // Root level object
    lv_img_set_src();  // Cause a splashscreen to appear

    // Begin user interaction, the real application
    lv_obj_t *next_scr = create_screen_2();
    lv_scr_load_anim(next_scr, LV_SCR_LOAD_ANIM_OVER, 250ms, 2000ms, true);

    // The third screen (not shown) appears due to a
    // event, which doesn't lead to the LVGL problem
    // of several screens appearing at the same time.
}

Control flow

The advice given by @embeddedt to implement control flow in button callbacks helps avoid the problems from my declarative (see my Screen 1/2/3/4) approach.

In order to use some type of asynchronous callback, can lv_async_call(3) be used? I don’t see a way to delay or serialise (according to your queue analogy) code in a lv_async_call(3).

Using an LVGL task might work, but only if the period argument is used by lv_task_once(3) to force a delay (rather than a period.)

lv_task_t *pMytask = lv_task_create_basic();
lv_task_set_period(pMytask, 2000ms);
lv_task_once(pMytask);

Questions

Do you think the above pseudocode block achieves the goal to define several screens and serialise their appearance?

Wouldn’t making four run-once tasks suffer the same problem, that they are scheduled to elapse their periods at the same time just like the concurrency of the original (lv_scr_load_anim with a delay) approach?

Migration to 8.X

By the way, it seems tasks (from 7.X) are called timers in trunk development, so this applies:

lv_timer_create(3);
lv_timer_ready(3);

Yes; you would have to start the next task from inside the first task. Or, you can just use one repeating task with a period of 2000ms, and change the screen each time it runs. When you reach the end of the list of screens, delete the task with lv_task_del/lv_timer_del.