Runtime rotation

Description

I need to rotate my screen in runtime just like cell phone. At the moment I’m not sure if I can change the scanning direction of my LCD in driver level. so for now I want to do this at software level.
I read the documentation -v7.11- and also the posts regarding this problem.

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

stm32f429, gcc, cubeide

What LVGL version are you using?

v7.11

What do you want to achieve?

runtime rotation of screen.

What have you tried so far?

I read the documentation and previous posts. and:
I added these lines to my lvgl_init function:

    disp_drv.sw_rotate = 1;
	disp_drv.rotated = LV_DISP_ROT_NONE;

and call something like this:

lv_disp_set_rotation(NULL, LV_DISP_ROT_90);

whenever I want to rotate.
My first question is:
Should the LV_HOR_RES_MAX and LV_VER_RES_MAX be equal in software rotation by lvgl?.

I ask this because when these are different it doesn’t work well.

For v7.11, I am pretty sure you will get away with not changing it in certain cases. When the display’s native orientation is landscape, the horizontal resolution can never be greater when it switches to portrait. (The vertical resolution does become greater, but LV_VER_RES_MAX is almost never used within LVGL so it doesn’t cause a big issue.)

However, I strongly suggest making them the same as it’s technically undefined behavior if you don’t, and with a display that is natively in portrait mode, you will not be so lucky.

(Thanks for making me research this; I realized my project’s maximum settings in lv_conf.h are actually wrong. :wink:)

1 Like

thanks dear @embeddedt

The resolution of my LCD is 400* 1280 and its native scanning direction is portrait.
I defined the hor_res and ver_res as 1280.
And also in ltdc_init I just set the image size of layer 1 as 1280 * 1280.
It seems working. (Not sure about its speed, becase I just created a simple obj.)

  1. So what do you think? Am I doing right by now?

  2. AFAIU, I should resize and realigne all of the objects on the screen every time I need to rotate. Right?

  3. @kisvegabor @embeddedt AFAIK, by now It is not posible to rotate objects in lvgl. But what if it would be posible? Is it hard to implement? What do you think?

Software rotation doesn’t require you to change anything in your display driver besides setting .sw_rotate = 1. All that’s needed is to change LV_HOR_RES_MAX and LV_VER_RES_MAX, as well as calling lv_disp_set_rotation.

You do not need to make your resolution 1280x1280.

Yes, if you aren’t using containers to position everything.

1 Like

thanks.

You do not need to make your resolution 1280x1280.

  • Do you mean in MX_LTDC_Init? or the definition of LV_HOR_RES_MAX and LV_VER_RES_MAX?

  • what do you think about my third question?

The only dimensions that needs to be adjusted are LV_HOR_RES_MAX and LV_VER_RES_MAX. The rest of your display driver can be left unchanged.

The proposed lv_snapshot feature in an older revision of the ROADMAP would have allowed for individual objects to be rotated (by internally rendering them on a canvas), but that feature has been silently removed in the latest roadmap revision.

@kisvegabor Any reasons for that?

1 Like

When I reorganized the ROADMAP it was removed accidentally. I’ve added it back. https://github.com/lvgl/lvgl/commit/923dbca934715b839a28ca51babdd116c45b1b98

The mentioned “snapshot” feature is planned to manage this. Real-time drawing e.g. a rotated rounded rectangle with gradient, border, shadow and a label on it is way too complicated for embedded targets and would use too much memory.
So non rotated rendering to an image + image rotation seems like a reasonable approach.

1 Like

so then what would be its disadvantages in comparison with real-time drawing?

From the application view, Is this feature and real-time drawing the same?

The main disadvantage is memory usage. You can’t rotate an object unless you have enough RAM to store a bitmap of it at your display’s color depth.

For example, to rotate a 100x20 object on a 16bpp screen, you would need 4KB of RAM available.

I think it’s a reasonable compromise, since you would probably be doing this for fancier GUIs on systems with enough memory to spare, and there is no additional cost if you are not rotating the object.

1 Like

Yeah, get it. thanks.

I upgraded to the master branch -ver 8- and this is my lvgl_init:

    lv_init();

	DMA_Config();

	const lv_coord_t hor_res_max = 1280;;
	const lv_coord_t ver_res_max = 1280;

	static lv_disp_draw_buf_t disp_buf;
	static lv_color_t buf[1280 * 50U];                     /*Declare a buffer for 200 lines*/
	lv_disp_draw_buf_init(&disp_buf, buf, NULL, hor_res_max * 50U);    /*Initialize the display buffer*/

	lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
	disp_drv.hor_res = hor_res_max;               /*Set the horizontal resolution*/
	disp_drv.ver_res = ver_res_max;               /*Set the vertical resolution*/
	disp_drv.flush_cb = ex_disp_flush;    /*Set your driver function*/
	disp_drv.draw_buf = &disp_buf;          /*Assign the buffer to the display*/
	disp_drv.sw_rotate = 1;
	disp_drv.rotated = LV_DISP_ROT_NONE;

And this is my MX_LTDC_Init function:

static void MX_LTDC_Init(void)
{

  /* USER CODE BEGIN LTDC_Init 0 */

  /* USER CODE END LTDC_Init 0 */

  LTDC_LayerCfgTypeDef pLayerCfg = {0};

  /* USER CODE BEGIN LTDC_Init 1 */

  /* USER CODE END LTDC_Init 1 */
  hltdc.Instance = LTDC;
  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  hltdc.Init.HorizontalSync = 23;
  hltdc.Init.VerticalSync = 1;
  hltdc.Init.AccumulatedHBP = 183;
  hltdc.Init.AccumulatedVBP = 11;
  hltdc.Init.AccumulatedActiveW = 583;
  hltdc.Init.AccumulatedActiveH = 1291;
  hltdc.Init.TotalWidth = 743;
  hltdc.Init.TotalHeigh = 1303;
  hltdc.Init.Backcolor.Blue = 0;
  hltdc.Init.Backcolor.Green = 0;
  hltdc.Init.Backcolor.Red = 0;
  if (HAL_LTDC_Init(&hltdc) != HAL_OK)
  {
    Error_Handler();
  }
  pLayerCfg.WindowX0 = 0;
  pLayerCfg.WindowX1 = 400;
  pLayerCfg.WindowY0 = 0;
  pLayerCfg.WindowY1 = 1280;
  pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  pLayerCfg.Alpha = 255;
  pLayerCfg.Alpha0 = 0;
  pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  pLayerCfg.FBStartAdress = FRAME_BUFFER_ADD;
  pLayerCfg.ImageWidth = 1280;
  pLayerCfg.ImageHeight = 1280;
  pLayerCfg.Backcolor.Blue = 0;
  pLayerCfg.Backcolor.Green = 0;
  pLayerCfg.Backcolor.Red = 0;
  if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LTDC_Init 2 */

  /* USER CODE END LTDC_Init 2 */

}

If I change

  pLayerCfg.ImageWidth = 1280;

to

  pLayerCfg.ImageWidth = 400;

It will not work properly. So @embeddedt what do you think now? Am I doing anything wrong?

400 should be working; I’m not sure why you’re having issues. What value did the original LTDC_Init function from before you started working with rotation use?

Does 400 work if you don’t enable rotation?

1 Like

If I change this line:

disp_drv.hor_res = 400;

it will work with this:

pLayerCfg.ImageWidth = 400;

properly in LV_DISP_ROT_NONE state or when I disable the software rotation.

You can leave hor_res as your display’s real horizontal resolution (400). There is no need to change it as LVGL will automatically figure out the value based on the rotation mode you provide.

With that in place pLayerCfg.ImageWidth = 400; should also work.

1 Like

But when I do this it doesn’t work in the LV_DISP_ROT_90 state.
Can you test it with an STM32 board and LVGL ver8?

I’ve just tested it again on v8 with my STM32 board and it works as I described.

    /*Set the resolution of the display*/
    disp_drv.hor_res = 480;
    disp_drv.ver_res = 272;
    disp_drv.sw_rotate = 1;

My display’s normal resolution is landscape, so I leave hor_res and ver_res configured that way and enable software rotation. Then, I just have to call lv_disp_set_rotation, and LVGL takes care of everything else.

I made no modifications to the ST-provided display initialization logic.

Thanks for your effort.
Although It works for me the way I said, I think it’s slow and can be faster if it works the way you said.

@kisvegabor what do you think about this? Is there anything that I can do to fix it?

With such a large display increasing LV_DISP_ROT_MAX_BUF size in lv_conf.h can improve the performance. The default 10k is enough only for 4 lines with 16 bit color depth.

1 Like

Yeah, I did it and it got far more better. Thanks

But what do you think about what @embeddedt said and what I have done.
Is there any thing wrong with lvgl?

I’ve tested it in simulator and it worked well for me:

  disp_drv.hor_res = 400;
  disp_drv.ver_res = 1000;
  disp_drv.sw_rotate = 1;
  disp_drv.rotated = LV_DISP_ROT_90;

On MCU how do you mean it’s not working: Messed up screen? Hard fault? Black screen?
Please also post your current lv_disp_drv_t and lv_disp_draw_buf_t initializtion code.

1 Like

Messed up screen in LV_DISP_ROT_90 state and works O.K in LV_DISP_ROT_NONE state.

I posted it above. anyway, I bring it again here:

void lvgl_init(void)
{
	lv_init();

	DMA_Config();

	const lv_coord_t hor_res_max = 400; //1280;
	const lv_coord_t ver_res_max = 1280;

	static lv_disp_draw_buf_t disp_buf;
	static lv_color_t buf[1280 * 50U];                     /*Declare a buffer for 200 lines*/
	lv_disp_draw_buf_init(&disp_buf, buf, NULL, 1280 * 50U);    /*Initialize the display buffer*/

	lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
	disp_drv.hor_res = hor_res_max;               /*Set the horizontal resolution*/
	disp_drv.ver_res = ver_res_max;               /*Set the vertical resolution*/
	disp_drv.flush_cb = ex_disp_flush;    /*Set your driver function*/
	disp_drv.draw_buf = &disp_buf;          /*Assign the buffer to the display*/
	disp_drv.sw_rotate = 1;
	disp_drv.rotated = LV_DISP_ROT_NONE;

#if 0
	/* Fill a memory array with a color if you have GPU.
	 * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
	 * But if you have a different GPU you can use with this callback.*/
	disp_drv.gpu_blend_cb = ex_mem_blend;              /*Blend two color array using opacity*/
	disp_drv.gpu_fill_cb = ex_mem_fill;                /*Fill a memory array with a color*/
	DMA2D_Config();
#endif

	lv_disp_drv_register(&disp_drv);/*Finally register the driver*/
}

and MX_LTDC_Init:

static void MX_LTDC_Init(void)
{

  /* USER CODE BEGIN LTDC_Init 0 */

  /* USER CODE END LTDC_Init 0 */

  LTDC_LayerCfgTypeDef pLayerCfg = {0};

  /* USER CODE BEGIN LTDC_Init 1 */

  /* USER CODE END LTDC_Init 1 */
  hltdc.Instance = LTDC;
  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  hltdc.Init.HorizontalSync = 23;
  hltdc.Init.VerticalSync = 1;
  hltdc.Init.AccumulatedHBP = 183;
  hltdc.Init.AccumulatedVBP = 11;
  hltdc.Init.AccumulatedActiveW = 583;
  hltdc.Init.AccumulatedActiveH = 1291;
  hltdc.Init.TotalWidth = 743;
  hltdc.Init.TotalHeigh = 1303;
  hltdc.Init.Backcolor.Blue = 0;
  hltdc.Init.Backcolor.Green = 0;
  hltdc.Init.Backcolor.Red = 0;
  if (HAL_LTDC_Init(&hltdc) != HAL_OK)
  {
    Error_Handler();
  }
  pLayerCfg.WindowX0 = 0;
  pLayerCfg.WindowX1 = 400;
  pLayerCfg.WindowY0 = 0;
  pLayerCfg.WindowY1 = 1280;
  pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  pLayerCfg.Alpha = 255;
  pLayerCfg.Alpha0 = 0;
  pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  pLayerCfg.FBStartAdress = FRAME_BUFFER_ADD;
  pLayerCfg.ImageWidth = 400;// 1280;
  pLayerCfg.ImageHeight = 1280;
  pLayerCfg.Backcolor.Blue = 0;
  pLayerCfg.Backcolor.Green = 0;
  pLayerCfg.Backcolor.Red = 0;
  if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LTDC_Init 2 */

  /* USER CODE END LTDC_Init 2 */

}