Display Rotation

Hello,

I’m working on an embedded system with a real time operating system (RTEMS). I have a simple (double buffered) frame buffer driver for a hardware that can only output the frame in exactly the direction it is in memory. I don’t have a rotation option in the hardware or on the display.

I tried to use the set_px_cb() in the driver to have the pixels at the right locations. That covered all cases for the lv_draw_basic.c. But now I noted that there is another file that can update the memory: The lv_refr.c.

Now I think about a number of possibilities (not sure yet which one will be used). One is to add a display rotation functionality to LittlevGL. As a rough direction I think about the following:

  • find all locations in lv_draw_basic.c and lv_refr.c that draw to the buffer
  • modify them to allow a rotation of 0, 90, 180 and 270 degree based on
    • either a #ifdef (better efficiency but less universal)
    • or a parameter in the display driver (less efficient but nice for multi display setups; although it’s no target for me, it maybe can be extended to support runtime rotation if someone want’s to add that in the future)

Would such an option be acceptable? If no I’ll strike that possibility from my list.

Best regards

Christian

Hi,

In not true double buffered mode (1 or 2 smaller buffer and frame buffer) you can use this:

I think @c-mauderer is looking to add this functionality to LittlevGL itself so people don’t have to implement it in their driver.

Thanks for this hint. Currently my status is that I will try a quick solution like that in a first run. But I have a quite big display of 800 x 480 Pixel. So this solution might lead to a high CPU load or it will lead to tearing effects. Depending on whether the results are acceptable or not I might need another solution.

That’s the second solution on my list. With my question I wanted to find out whether such a solution would be something that could be accepted as a patch to LittlevGL or not. Depending on that (and if my first approach doesn’t work) I’ll put more work into the topic to write a clean solution or less work to just add a quick hack on a private fork.

The rotation feature was asked several times but the problem is that “thinking in lines” approach is deeply wired in lvgl. I think it’d be very difficult to add “column-based thinking” too.

Although the CPU load is quite high for a 800x480 display (about 15% of a few 100MHz CPU in case of a lot of activity on the screen), I currently can rotate in software. As long as I don’t hit the limit of my system, I’ll use that solution for now.

If there is time left in the project for optimization or if the CPU load is too high, I’ll come back to the more complex solution of implementing it in LittlevGL.

Edit: Note that I accepted the first solution suggesting a software rotation so that the post is closed.

Alright, thank you in advance! :slight_smile:

Hi, there are news of implementing rotation in LittlevGL?
Thanks!

I didn’t hit the limit of the system and the project turned out to be a lot more work at another software part. So I never had the time to come back to this topic. So it’s still a rotation in the driver for me. Sorry.

Hi to all,
I tried the code and I would say it works perfectly. I attach the link to the video.
This is a custom board with PIC32MZDA 200Mhz, RGBA intewrface 800x480 TFT

@embeddedt
Hi Embedded, ask you if can you help me, i’m trying to rotate of 270 degree and i have get this
code that rotate display of 90 degree, but i can’t undestand how modify. If you had any suggestions

thank you

void get_col(const lv_color_t * src, const lv_area_t * area, lv_coord_t y, lv_color_t * dest)
{
    lv_coord_t w = lv_area_get_width(area);
    lv_coord_t h = lv_area_get_height(area);

    lv_coord_t i;
    for(i = 0; i < h; i++) {
        dest[i].full = src[y].full;
        src += w ;
    }
}

void monitor_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) 
{
    lv_coord_t x2 = area->x2;
    if(x2 >= disp_drv->ver_res) x2 = MONITOR_VER_RES - 1;

    lv_color_t * dest = (lv_color_t *)monitor.tft_fb;
    dest +=  MONITOR_HOR_RES * (MONITOR_VER_RES - 1);
    dest -= MONITOR_HOR_RES * area->x1;

    lv_coord_t x;
    for(x = area->x1; x <= x2; x++) {
          get_col(color_p, area, (x - area->x1), dest + area->y1);
          dest -= MONITOR_HOR_RES;
    }
}

Rotation means clockwise 90 or 270 degrees.

Change the #define ROTATE accordingly

And also take care about the frame buffer base address, which is 0xd0000000 on my system.
Change it accordingly

#define ROTATE 90

/*****   get_col_270 (...)                                  *******************/
/*! @brief Copy all source column pixels into destination area
 *
 * @param[in] src    The pixel source buffer
 * @param[in] area   The rectangle (x1, y1 -> x2, y2) within the destination buffer
 * @param[in] x      The x pos int source area
 * @param[in] dest   The destination buffer address
 *
 * We read from the source area the same x pos for every line
 * and write to destination address sequentially in positiv direction
 *
 */
void get_col_270 (const lv_color_t* src, const lv_area_t* area, lv_coord_t x, lv_color_t* dest)
{
    lv_coord_t  col;
    lv_coord_t  w = lv_area_get_width (area);              // The width of the source area
    lv_coord_t  h = lv_area_get_height (area);             // The height of the source area

    for (col = 0; col < h; col++) {                        // all source column pixels (line in destination)
        dest[col].full = src[x].full;                      // The x pos into source area
        src           += w;                                // Set for the next source line
    }
}

/*****   get_col_90 (...)                                   *******************/
/*! @brief Copy all source column pixels into destination area
 *
 * @param[in] src    The pixel source buffer
 * @param[in] area   The rectangle (x1, y1 -> x2, y2) within the destination buffer
 * @param[in] x      The x pos int source area
 * @param[in] dest   The destination buffer address
 *
 * We read from the source area the same x pos for every line
 * and write to destination address sequentially in negative direction
 *
 */
void get_col_90 (const lv_color_t* src, const lv_area_t* area, lv_coord_t x, lv_color_t* dest)
{
    lv_coord_t  col;
    lv_coord_t  w = lv_area_get_width (area);              // The width of the source area
    lv_coord_t  h = lv_area_get_height (area);             // The height of the source area

    for (col = 0; col < h; col++) {                        // all source column pixels (line in destination)
        dest[-col].full = src[x].full;                     // The -x pos into source area
        src            += w;                               // Set for the next source line
    }
}

/*****   monitor_flush (...)                                *******************/

void monitor_flush (lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
    lv_coord_t x2 = area->x2;

    if (x2 >= disp_drv->hor_res) {
        x2 = disp_drv->hor_res - 1;
    }

#if ROTATE == 270

    lv_color_t* dest = (lv_color_t*) 0xd0000000;                 // monitor.tft_fb;         // destination buffer base address

    dest += disp_drv->ver_res * (disp_drv->hor_res - 1);         // calculate the start of the very last line in destination buffer
    dest -= disp_drv->ver_res * area->x1;                        // and go back to start address of line

    lv_coord_t x;

    for (x = area->x1; x <= x2; x++) {                           // For all source columns
        get_col_270 (color_p,                                    // The pixel source buffer
                     area,                                       // The destination area we should write
                     (x - area->x1),                             // The x pos related to source area (is the y for destination)
                     dest + area->y1);                           // Destination buffer start address
        dest -= disp_drv->ver_res;                               // Next destination buffer start address (runs bottom to top)
    }

#elif ROTATE == 90

    lv_color_t* dest = (lv_color_t*) 0xd0000000;                 // monitor.tft_fb;         // destination buffer base address

    dest += disp_drv->ver_res * area->x1;                        // Start address of line

    lv_coord_t x;

    for (x = area->x1; x <= x2; x++) {                           // For all source columns
        get_col_90 (color_p,                                     // The pixel source buffer
                    area,                                        // The destination area we should write
                    (x - area->x1),                              // The x pos related to source area (is the y for destination)
                    dest + (disp_drv->ver_res - 1 - area->y1));  // Destination buffer start address
        dest += disp_drv->ver_res;                               // Next destination buffer start address (runs top to bottom)
    }

#endif

    lv_disp_flush_ready (disp_drv);                              // IMPORTANT! It must be called to tell the system the flush is finished
}

1 Like

Thank you for reply and example, i tried your code and now i can rotate in all 4 ways.

How are u able to do software rotation?.. could you plz share the code?

Display rotation is already supported in LVGL. See Display interface — LVGL documentation

I have already referred the documentation. I want to rotate NXP imxrt1170 display from portrait to landscape mode(90 degree rotation). I have implemented the code as told in LVGL documentation. There is no error showing up, but no rotation happens.

For rotation, is it necessary to call the buffer and display driver?
I am using Lvgl-8. When I change the LV_HOR_RES_MAX and LV_VER_RES_MAX, the rotation happens in the simulator. But not into the imxrt display.

I am new to Lvgl. Please forgive me if I mention anything wrongly.

Hi,

I guess the problem is this limitation: https://github.com/lvgl/lvgl/blob/master/src/core/lv_refr.c#L1145

So does your driver use disp_drv->full_refresh=1? If so try using changing the driver to something like this and use disp_drv->full_refresh=0: https://github.com/lvgl/lv_drivers/blob/462d9cdeb83df9838f3f02e6f156fc37599c9add/sdl/sdl.c#L165-L170