Screen rotation

Description

I try to rotate the display in the same orientation as the touchscreen. When I use the lv_display_set_rotation() function, my display is rotated, but the screen width is truncated to 240 pixels, while it should be 320 pixels. In fact, I’m getting a 240 x 240 display. When I use the corresponding TF_eSPI function tft.setRotation(), the display rotation is performed as expected.
I tried different rotation settings. The content of the display is rotated as required, but the display is always 240x240.

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

CYD = ESP32 with XPT2046 touch controler and ILI9341 display controller

What LVGL version are you using?

9.2

What do you want to achieve?

support rotation in all 4 orientations

What have you tried so far?

see description section
I also tried without swapping height and width in the #define section, but the result is the same.

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

/*You code here*/

#define LV_DISPLAY_ROTATION_0 0 // portrait, USB bottom side
#define LV_DISPLAY_ROTATION_90 1 // landscape, USB right side
#define LV_DISPLAY_ROTATION_180 2 // portrait, USB top side
#define LV_DISPLAY_ROTATION_270 3 // landscape USB left side

#define TFT_ROTATION LV_DISPLAY_ROTATION_90 // landscape, USB right side

// swap height and width according to rotation setting

#if (TFT_ROTATION == LV_DISPLAY_ROTATION_0) || (TFT_ROTATION == LV_DISPLAY_ROTATION_180)
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#else
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#endif

… some more code

in setup function:
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(TFT_ROTATION);
… some more code …
disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
// lv_display_set_rotation(disp, (lv_display_rotation_t)TFT_ROTATION); // use LVGL lib to set rotation doesn’t work!
tft.setRotation(TFT_ROTATION); // use TFT lib to set rotation

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

What about ui elements configuration - for example active screen size ? Is there screen-object size reconfiguarion in your code after rotation activation ?

Need to do more testing to sort this out. I replaced the tft.rotateScreen() function with lv_display_set_rotation(). Now it is working (kindof…). My button on the screen is rotated, but the touch area which belongs to that button is not rotated. Maybe I have to destroy the existing button and simply create it again after rotating?
Note: my initial thought to use the tft function iso the lvgl function to rotate the screen comes from the fact that the lvgl function did not work for the initial totation setting in setup(). the tft function did the job. Just out of curiosity I enabled the lvgl rotation function again after doing the tft rotation is setup() and my screen remains intact. I know it’s silly to set the same rotation using 2 different functions, but I just wanted to know if the lvgl rotation is working or not. After setting the rotation using tft I can use the lvgl function for any subsequent rotation (except for the fact that the touch areas aer not rotated with their visual object (like a button). Looks like something is not properly initialized upon first use?

I have made a sample code for the Lilygo T-Display S3. The display rotation works very well.
See here

Thomas, thanks for trying, but I’m working with CYD and ESP32 on Arduino IDE.

Meanwhile I started from scratch again. First a few tutorial sketches with the basics for CYD and ESP32. No problems. Then I started using the example which comes with the Arduino (v 2.3.4) IDE: File/examples/lgvl/arduino/LGVL_Arduino. The sketch is basically a template with suggestions how to add lgvl-related funtions to it. Went OK up to the point where I’m mapping my touch points to display points. Still OK for all rotation values. Next step in that function (my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data ) is to pass the display points x/y to lvgl indev function by populating data->point.x and data->point.y with the diaply coordinates of my touch point. And that’s where things seem to wrong. Either I’m passing bad data to LVGL or something is very wrong with the way how LVGL Indev() is processing the data. I’ts probably me, but either way, I’m stuck.
When I follow the Random Nerd Tutorials in their CYD eBook, things don’t work the way I expect (results on display look OK, but serial port in the IDE is drowning in error messages) using their code and I’m going totally overboard when I try to adapt their sketch for a different orientation. Looks like setting rotation for touch screen, lvgl and my user code are fighting each other.
Small consolation: looks like I’m not the only one having trouble using screen rotation with LGVL.
Code:
``
/Read the touchpad/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
// Checks if Touchscreen was touched, and reports Display.x, Display.y and Touch.Pressure (z)
if(touchscreen.tirqTouched() && touchscreen.touched()) {

// Get RAW Touchscreen points

TS_Point p = touchscreen.getPoint();

// translate RAW TS points to calibrated Display points

switch(TFT_ROTATION) {
  case LV_DISPLAY_ROTATION_0 :                                              // rotation = 0 - portrait, USB @ bottom side
    x = map(p.x, 280, 3850, 0, TFT_WIDTH);
    y = map(p.y, 200, 3730, 0, TFT_HEIGHT);
    x = constrain(x, 0, TFT_WIDTH - 1);
    y = constrain(y, 0, TFT_HEIGHT - 1);
    break;

  case LV_DISPLAY_ROTATION_90:                                              // rotation = 1 - landscape, USB @ right side
    x = map(p.x, 180, 3750, 0, TFT_HEIGHT);
    y = map(p.y, 275, 3850, 0, TFT_WIDTH);
    x = constrain(x, 0, TFT_HEIGHT - 1);
    y = constrain(y, 0, TFT_WIDTH - 1);
    break;

  case LV_DISPLAY_ROTATION_180:                                             // rotation = 2 - portrait, USB @ top side
    x = map(p.x, 275, 3850, 0, TFT_WIDTH);
    y = map(p.y, 350, 3900, 0, TFT_HEIGHT);
    x = constrain(x, 0, TFT_WIDTH - 1);
    y = constrain(y, 0, TFT_HEIGHT - 1);
    break;

  case LV_DISPLAY_ROTATION_270:                                             // rotation = 3 - landscape, USB @ left side
    x = map(p.x, 380, 3800, 0, TFT_HEIGHT);
    y = map(p.y, 260, 3780, 0, TFT_WIDTH);
    x = constrain(x, 0, TFT_HEIGHT - 1);
    y = constrain(y, 0, TFT_WIDTH - 1);
    break;

  default:
    Serial.printf("Error: invalid rotation value = %d", TFT_ROTATION);
    x = 0;
    y = 0;
    break;
}

Serial.printf("\ntouch points p.x, p.y mapped to x = %d, y = %d\n", x, y);

// pass those DP points to indev.data

data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;

}
else{
data->state = LV_INDEV_STATE_RELEASED;
}
}

Hmm i dont understand your query. Indev cb isnt part of LVGL. Manage rotation require special display and special touch code. Plus map used x and p.x require after rotatio x and p.y …

Hi Marian_M,
the use of the indev callback is documented here: Input Device (lv_indev) — LVGL documentation. It is also present in many examples and tutorials, a.o. in the (only) example contained in the Arduino IDE (v 2.3.4).
I’m using LVGL version 9.2.2. Maybe you’re referring to an older version?
Meanwhile I’m testing what seems to be a fix. I’ll get back to this topic when I can confirm that my fix does the job.
If you can elaborate on your suggestion about special display and special touch code, please show a code example. I haven’t seen anything special in the tutorials. All tuto’s I’ve seen map or calibrate the x/y values before populating the data->x/y values.

Yes documented is as ready to use interface, but no working code. As you can see i mark it for you

and here in EXAMPLE no chaos map or orientation. And too here example dont show how touchpad_x is get.
Seems many peaple mean, that example code is for skip learning .

Been there, tried that and it doesn’t work.
When I feed the data->point struct with the touch coordinates, I get an endless stream of warnings from indev:

23:39:27.210 → [Warn] (8.310, +30) indev_pointer_proc: X is 2143 which is greater than hor. res lv_indev.c:699
23:39:27.243 → [Warn] (8.310, +30) indev_pointer_proc: X is 2143 which is greater than hor. res lv_indev.c:699

This confirms my suspicion that indev expects display coordinates, not touch coordinates.
Looks like a bug to me. But I’m an LGVL noob…
When I create a macro #define LV_INDEV_POINTER_ROTATED and bypass the rotation code in lv_indev.c like this (lines 679…)

#ifndef LV_INDEV_POINTER_ROTATED

    if(disp->rotation == LV_DISPLAY_ROTATION_180 || disp->rotation == LV_DISPLAY_ROTATION_270) {
        data->point.x = disp->hor_res - data->point.x - 1;
       data->point.y = disp->ver_res - data->point.y - 1;
    }

    if(disp->rotation == LV_DISPLAY_ROTATION_90 || disp->rotation == LV_DISPLAY_ROTATION_270) {
        int32_t tmp = data->point.y;
        data->point.y = data->point.x;						// swap x, y
        data->point.x = disp->ver_res - tmp - 1;
    }

#endif

then all tuto’s work like they are supposed to.
I may be completely wrong, but then I’d like to see some code that really works on a CYD device, nut just an example code that obviously hasn’t been tried :wink:

Finally sorted it…
The function indev_pointer_proc(…) rotates the display coordinates calculated by the user’s code which maps touchscreen coordinates to display coordinates.
But there are two issues:
(1) It is assumed that the input/output mapping is done based on the default orientation (= portrait, origin of input and output in the top left corner), regardless of the actual rotation setting for the display. The indev_pointer_proc(…) function will then transpose those display coordinates to the display coordinates which belong to the currently set rotation for the display.
(2) The documentation in “Input Device (lv_indev) — LVGL documentation” is incorrect. The example on that page says:

if(touchpad_pressed) {
    data->point.x = **touchpad_x**;
    data->point.y = **touchpad_y**;
    data->state = LV_INDEV_STATE_PRESSED;
} else {
    data->state = LV_INDEV_STATE_RELEASED;
}

This suggests that the x/y values passed to the data struct are touchpad x/y values. But the values processed by that function are display x/y values!

Recommendation:
1 - fix the documentation by using a correct code example
2 - add a warning to the documentation that final rotation is performed by LVGL, hence the input device (i.e. the touch screen) must NOT be rotated to the same value as the output defvice (i.e. the display). If needed the input device can be rotated (usually by 180°) to put the origin for the input device in the same corner as the output device, again without additional rotation to synchronize with the rotation (if any) of the display.

There is more to it…
The code in function
static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
in file …\Arduino\libraries\lvgl\src\indev\lv_indev.c
doesn’ do the rotation correcly.
Fix:
(1) fix the documentation by emphasizing that the touchscreen and the display must have their origin in the same corner (upper left corner). If that’s not the case rotate the touchscreen 180° using the function touchscreen.setRotation(2).
(2) Never rotate the touchscreen when rotating the display.
(3) Fix the code. replace this section of code in file “\lv_indev.c”

    if(disp->rotation == LV_DISPLAY_ROTATION_180 || disp->rotation == LV_DISPLAY_ROTATION_270) {		// mirror x,y
        data->point.x = disp->hor_res - data->point.x - 1;
        data->point.y = disp->ver_res - data->point.y - 1;
    }

    if(disp->rotation == LV_DISPLAY_ROTATION_90 || disp->rotation == LV_DISPLAY_ROTATION_270) {			// swap x, y
        int32_t tmp = data->point.y;
        data->point.y = data->point.x;
        data->point.x = disp->ver_res - tmp - 1;
    }

with this:

switch(disp->rotation) {
	case LV_DISPLAY_ROTATION_90:
		tmp = data->point.y;
		data->point.y = disp->hor_res - data->point.x - 1;
		data->point.x = tmp;
	break;
		
	case LV_DISPLAY_ROTATION_180:
		data->point.x = disp->hor_res - data->point.x - 1;
		data->point.y = disp->ver_res - data->point.y - 1;
	break;
		
	case LV_DISPLAY_ROTATION_270:
		tmp = data->point.y;
		data->point.y = data->point.x;
		data->point.x = disp->ver_res - tmp - 1;
	break;
	}