Skip to content

rgb_display rotate error #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jd3096-mpy opened this issue Sep 7, 2024 · 19 comments
Closed

rgb_display rotate error #115

jd3096-mpy opened this issue Sep 7, 2024 · 19 comments

Comments

@jd3096-mpy
Copy link

jd3096-mpy commented Sep 7, 2024

Rotaion screen by
display.set_rotation(lv.DISPLAY_ROTATION._90)

https://github.com/lvgl-micropython/lvgl_micropython/blob/main/api_drivers/common_api_drivers/display/rgb_display.py
line 17
self._disp_drv.set_orientation(value)
shoule be
self._disp_drv.set_rotation(value) ??

I modify and it show like this ,still some mistake:
e5c54e637921489ce311fe00139f076

I test st7796 too, it works good.
By the way,you haven't send me address yet.

@jd3096-mpy jd3096-mpy changed the title rgb_display rotation error rgb_display rotate error Sep 7, 2024
@zjalen
Copy link

zjalen commented Sep 8, 2024

Hello, I think I have the same board with you. But I have some trouble with gt911 init. Could you tell me which commit version you use and show me an example?Here is my code, but my screen feedback is not in the correct position.

import time, os, urandom, lcd_bus, rgb_display, lvgl as lv
from machine import Pin, SPI, ADC, PWM, SDCard, SoftSPI
from micropython import const
import i2co as i2c   # checkout from develop branch
import gt911o as gt911   # checkout from develop branch
import task_handler
import _thread

# from demo import Demo
# d=Demo()


class Demo(object):

    def __init__(self):
        _WIDTH, _HEIGHT = 800, 480
        self.indev = None
        self.cnt = 0
        self.rgb_bus = lcd_bus.RGBBus(de=40, vsync=41, hsync=39, pclk=42,
                                      data11=45, data12=48, data13=47, data14=21, data15=14,
                                      data5=5, data6=6, data7=7, data8=15, data9=16, data10=4,
                                      data0=8, data1=3, data2=46, data3=9, data4=1,
                                      freq=13000000, hsync_front_porch=8, disp=-1,
                                      hsync_pulse_width=4, hsync_back_porch=8, hsync_idle_low=True,
                                      vsync_front_porch=8, vsync_pulse_width=4, vsync_back_porch=8,
                                      vsync_idle_low=True, de_idle_high=False, pclk_idle_high=False,
                                      disp_active_low=False, refresh_on_demand=False, pclk_active_low=1)

        _BUF1 = self.rgb_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
        _BUF2 = self.rgb_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
        self.display = rgb_display.RGBDisplay(data_bus=self.rgb_bus, display_width=_WIDTH, display_height=_HEIGHT,
                                              frame_buffer1=_BUF1, frame_buffer2=_BUF2, backlight_pin=2,
                                              color_space=lv.COLOR_FORMAT.RGB565, rgb565_byte_swap=False)
        self.display.set_power(True)
        self.display.init()

        self.display.set_backlight(100)
        self.task_handler = task_handler.TaskHandler()

        self.screen = lv.screen_active()

        self.test2()
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0xff, 0, 0), 0)
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0, 0xff, 0), 0)
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0, 0, 0xff), 0)
        time.sleep_ms(500)

        self.init_touch()

    def init_touch(self):
        _WIDTH = const(800)
        _HEIGHT = const(480)

        i2c_bus = i2c.I2CBus(host=1, scl=20, sda=19)
        self.indev = gt911.GT911(i2c_bus)
        if self.indev.hw_size != (_WIDTH, _HEIGHT):
            fw_config = self.indev.firmware_config
            fw_config.width = _WIDTH
            fw_config.height = _HEIGHT
            fw_config.save()

    def test2(self):
        btn = lv.button(self.screen)
        btn.align(lv.ALIGN.CENTER, 0, 0)
        btn.add_event_cb(self.event_cb, lv.EVENT.CLICKED, None)
        label = lv.label(btn)
        label.set_text('Hello World!')

    def event_cb(self, e):
        print("Clicked")

        btn = e.get_target_obj()
        label = btn.get_child(0)
        label.set_text(str(self.cnt))
        self.cnt += 1
image

This is the output result, but screen feedback is not in the correct position.

>>> from demo import Demo
>>> d=Demo()
Touch Product id: bytearray(b'911\x00')
Touch Firmware version: 0x1060
Touch Vendor id: 0x0
Touch resolution: width=480, height=272
Clicked

My lvgl-mircopython git log version is 78cc29392253fa6e1054f1e93ca402380b6e2889.I tried to init the gt911 failed.The product_id is none.So I checked out the develop version to replace them.

import i2co as i2c   # checkout from develop branch
import gt911o as gt911   # checkout from develop branch

==========
I tried the newest lvgl-micropython master branch but touch did not work.Here is my code with the newest master version.

import i2c
import gt911
...
    def init_touch(self):
        _WIDTH = const(800)
        _HEIGHT = const(480)

        i2c_bus = i2c.I2C.Bus(host=1, scl=20, sda=19)
        touch_i2c = i2c.I2C.Device(i2c_bus, gt911.I2C_ADDR, gt911.BITS)
        indev = gt911.GT911(touch_i2c, reset_pin=None, startup_rotation=0)
        if indev.hw_size != (_WIDTH, _HEIGHT):
            fw_config = indev.firmware_config
            fw_config.width = _WIDTH
            fw_config.height = _HEIGHT
            fw_config.save()

This is the output result and the touch is not working.

>>> from demo import Demo
>>> d=Demo()
Touch Product id:
Touch Firmware version: 0x0
Touch Vendor id: 0x0
Touch resolution: width=0, height=0
Touch Product id:
Touch Firmware version: 0x0
Touch Vendor id: 0x0
Touch resolution: width=0, height=0

I hope you can give me some advice, I would be very grateful.

@jd3096-mpy
Copy link
Author

Hello, I think I have the same board with you. But I have some trouble with gt911 init. Could you tell me which commit version you use and show me an example?Here is my code, but my screen feedback is not in the correct position.

import time, os, urandom, lcd_bus, rgb_display, lvgl as lv
from machine import Pin, SPI, ADC, PWM, SDCard, SoftSPI
from micropython import const
import i2co as i2c   # checkout from develop branch
import gt911o as gt911   # checkout from develop branch
import task_handler
import _thread

# from demo import Demo
# d=Demo()


class Demo(object):

    def __init__(self):
        _WIDTH, _HEIGHT = 800, 480
        self.indev = None
        self.cnt = 0
        self.rgb_bus = lcd_bus.RGBBus(de=40, vsync=41, hsync=39, pclk=42,
                                      data11=45, data12=48, data13=47, data14=21, data15=14,
                                      data5=5, data6=6, data7=7, data8=15, data9=16, data10=4,
                                      data0=8, data1=3, data2=46, data3=9, data4=1,
                                      freq=13000000, hsync_front_porch=8, disp=-1,
                                      hsync_pulse_width=4, hsync_back_porch=8, hsync_idle_low=True,
                                      vsync_front_porch=8, vsync_pulse_width=4, vsync_back_porch=8,
                                      vsync_idle_low=True, de_idle_high=False, pclk_idle_high=False,
                                      disp_active_low=False, refresh_on_demand=False, pclk_active_low=1)

        _BUF1 = self.rgb_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
        _BUF2 = self.rgb_bus.allocate_framebuffer(_WIDTH * _HEIGHT * 2, lcd_bus.MEMORY_SPIRAM)
        self.display = rgb_display.RGBDisplay(data_bus=self.rgb_bus, display_width=_WIDTH, display_height=_HEIGHT,
                                              frame_buffer1=_BUF1, frame_buffer2=_BUF2, backlight_pin=2,
                                              color_space=lv.COLOR_FORMAT.RGB565, rgb565_byte_swap=False)
        self.display.set_power(True)
        self.display.init()

        self.display.set_backlight(100)
        self.task_handler = task_handler.TaskHandler()

        self.screen = lv.screen_active()

        self.test2()
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0xff, 0, 0), 0)
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0, 0xff, 0), 0)
        time.sleep_ms(500)
        self.screen.set_style_bg_color(lv.color_make(0, 0, 0xff), 0)
        time.sleep_ms(500)

        self.init_touch()

    def init_touch(self):
        _WIDTH = const(800)
        _HEIGHT = const(480)

        i2c_bus = i2c.I2CBus(host=1, scl=20, sda=19)
        self.indev = gt911.GT911(i2c_bus)
        if self.indev.hw_size != (_WIDTH, _HEIGHT):
            fw_config = self.indev.firmware_config
            fw_config.width = _WIDTH
            fw_config.height = _HEIGHT
            fw_config.save()

    def test2(self):
        btn = lv.button(self.screen)
        btn.align(lv.ALIGN.CENTER, 0, 0)
        btn.add_event_cb(self.event_cb, lv.EVENT.CLICKED, None)
        label = lv.label(btn)
        label.set_text('Hello World!')

    def event_cb(self, e):
        print("Clicked")

        btn = e.get_target_obj()
        label = btn.get_child(0)
        label.set_text(str(self.cnt))
        self.cnt += 1
image This is the output result, but screen feedback is not in the correct position.
>>> from demo import Demo
>>> d=Demo()
Touch Product id: bytearray(b'911\x00')
Touch Firmware version: 0x1060
Touch Vendor id: 0x0
Touch resolution: width=480, height=272
Clicked

My lvgl-mircopython git log version is 78cc29392253fa6e1054f1e93ca402380b6e2889.I tried to init the gt911 failed.The product_id is none.So I checked out the develop version to replace them.

import i2co as i2c   # checkout from develop branch
import gt911o as gt911   # checkout from develop branch

========== I tried the newest lvgl-micropython master branch but touch did not work.Here is my code with the newest master version.

import i2c
import gt911
...
    def init_touch(self):
        _WIDTH = const(800)
        _HEIGHT = const(480)

        i2c_bus = i2c.I2C.Bus(host=1, scl=20, sda=19)
        touch_i2c = i2c.I2C.Device(i2c_bus, gt911.I2C_ADDR, gt911.BITS)
        indev = gt911.GT911(touch_i2c, reset_pin=None, startup_rotation=0)
        if indev.hw_size != (_WIDTH, _HEIGHT):
            fw_config = indev.firmware_config
            fw_config.width = _WIDTH
            fw_config.height = _HEIGHT
            fw_config.save()

This is the output result and the touch is not working.

>>> from demo import Demo
>>> d=Demo()
Touch Product id:
Touch Firmware version: 0x0
Touch Vendor id: 0x0
Touch resolution: width=0, height=0
Touch Product id:
Touch Firmware version: 0x0
Touch Vendor id: 0x0
Touch resolution: width=0, height=0

I hope you can give me some advice, I would be very grateful.

Maybe your Touch resolution 480x272 is different from screen resolution 800x480? I guess,I don't use touch actually.

@jd3096-mpy
Copy link
Author

jd3096-mpy commented Sep 8, 2024

Now I now this rotate error is cause by st7701 don't support hardware rotate, it just can be rotated by software.
sw rotation seems hard in micropython.
In c language ,just add
disp_drv.sw_rotate = 1; // add for rotation
disp_drv.rotated = LV_DISP_ROT_270; // add for rotation
It works, but how to rotate in micropython?

@jd3096-mpy
Copy link
Author

    def _flush_cb(self, _, area, color_p):
        rotated_buf=color_p
        
        rotated_w = area.x2-area.x1+1
        rotated_h = area.y2-area.y1+1
        
        
        lv.draw_sw_rotate(rotated_buf, color_p, rotated_w, rotated_h, lv.draw_buf_width_to_stride(rotated_w,lv.COLOR_FORMAT.RGB565), lv.draw_buf_width_to_stride(rotated_h,lv.COLOR_FORMAT.RGB565), lv.DISPLAY_ROTATION._180, lv.COLOR_FORMAT.RGB565)
        x1 = area.x1 + self._offset_x
        x2 = area.x2 + self._offset_x

        y1 = area.y1 + self._offset_y
        y2 = area.y2 + self._offset_y


        size = (
            (x2 - x1 + 1) *
            (y2 - y1 + 1) *
            lv.color_format_get_size(self._color_space)
        )

        cmd = self._set_memory_location(x1, y1, x2, y2)


        # we have to use the __dereference__ method because this method is
        # what converts from the C_Array object the binding passes into a
        # memoryview object that can be passed to the bus drivers
        data_view = rotated_buf.__dereference__(size)
        self._data_bus.tx_color(cmd, data_view, x1, y1, x2, y2)

I tried to modify flush_cb,but it's still somthing wrong
I think it's about data_bus.tx_color
lvgl/lvgl#6211

@kdschlosser
Copy link
Collaborator

The st7701 display DOES support hardware rotation but because we have not gotten the SPI portion of the code working yet you are going to be limited to software rotation.

I am working on the software rotation part of the code. LVGL has removed this functionality in version 9.x and I am messing about with working around the problem using a different mechanism. I should have a fix for the software rotation today....

@jd3096-mpy
Copy link
Author

jd3096-mpy commented Sep 8, 2024

The st7701 display DOES support hardware rotation but because we have not gotten the SPI portion of the code working yet you are going to be limited to software rotation.

I am working on the software rotation part of the code. LVGL has removed this functionality in version 9.x and I am messing about with working around the problem using a different mechanism. I should have a fix for the software rotation today....

I checked the ST7701 manual, and even if the SPI is working properly, there doesn't seem to be any command to rotate the display. It appears that rotation for RGB screens is typically done through software. However, I came up with a good method to achieve rotation using the following two statements. But I don't know how to integrate them into the source code to generate the MicroPython firmware. I hope this can give you an idea.

esp_lcd_panel_swap_xy(panel_handle, true);
esp_lcd_panel_mirror(panel_handle,true,false);

By combining parameters, it is possible to achieve rotation in four directions. I tested it in ESP-IDF, and it works effectively.

@kdschlosser
Copy link
Collaborator

I have to double check the spec sheet for the display to see if it is able to be done. I know there is the COLMOD command, I am not sure if there is a MADCTL command to handle it.

But for the time being I just pushed a commit that will hopefully correct the issue with the software rotation for the RGB displays.

@kdschlosser
Copy link
Collaborator

using the esp_lcd functions is not possible because I am NOT using the display drivers that are available from the component registry. I want the display drivers to reside in Python code. The RGB display is the only exception to that because the panel driver and the bus driver are all rolled into one and I don't have a choice. It would have been nicer if it was written so the bus and the panel code was separated but it is not and the only way for me to deal with it is to do what I have done or write a whole new low level bus driver for the RGB displays. Trust me I have really thought about rewriting the driver...

The other thing when dealing with software rotation is it is going to be faster if the bytes are written to the buffer in the correct order instead of having to rearrange them after the fact. That is what I am trying to achieve doing it the way I am in the last commit.

Check it out and see if it works.

@kdschlosser
Copy link
Collaborator

I just checked the datasheet for the ST7701S and it in fact DOES support hardware rotation....

check it out

https://focuslcds.com/wp-content/uploads/Drivers/ST7701S.pdf#page=214

@kdschlosser
Copy link
Collaborator

edit that last comment. It supports the command but not setting the bits that allow the rotation to occur.

@jd3096-mpy
Copy link
Author

I just checked the datasheet for the ST7701S and it in fact DOES support hardware rotation....

check it out

https://focuslcds.com/wp-content/uploads/Drivers/ST7701S.pdf#page=214

image
Typically, MADCTL requires setting 3 bits to achieve rotation in four directions. However, the ST7701's MADCTL can only set 1 bit, so it cannot achieve four-direction rotation. It can only rotate by 180 degrees at most.

@jd3096-mpy
Copy link
Author

edit that last comment. It supports the command but not setting the bits that allow the rotation to occur.

I think this code it easier.
lv.screen_active().set_style_transform_rotation(900, 0)
lv.screen_active().set_pos(480,0)

Although it's relatively slow and the frame rate has decreased, it is indeed a feasible method.
However, I still believe that modifying the driver code, similar to how it's done in esp_lcd_panel, would be a better approach.

@kdschlosser
Copy link
Collaborator

the problem with using that method is if the user changes out the screen for a different one because the rotation will default back to what it was...

no one said that software rotation isn't slow. it is very slow. I don't know how LVGL handles rendering a rotated object. If there is a drop in performance then it must be rotating it after it has been rendered and not while it is being rendered. I was hoping it was the latter but it appears not. I can write some C code to handle the rotation and it would be faster than what LVGL does because LVGL's rotation is not written to handle angle changes that are locked to 90° steps.

Have you tried the code in the repo? If you have does it work?. I did just update the code again to change the anchor point and to fix a problem I had in it. I also relocated the code to a different location.

Give it a try and see if it works.

@jd3096-mpy
Copy link
Author

the problem with using that method is if the user changes out the screen for a different one because the rotation will default back to what it was...

no one said that software rotation isn't slow. it is very slow. I don't know how LVGL handles rendering a rotated object. If there is a drop in performance then it must be rotating it after it has been rendered and not while it is being rendered. I was hoping it was the latter but it appears not. I can write some C code to handle the rotation and it would be faster than what LVGL does because LVGL's rotation is not written to handle angle changes that are locked to 90° steps.

Have you tried the code in the repo? If you have does it work?. I did just update the code again to change the anchor point and to fix a problem I had in it. I also relocated the code to a different location.

Give it a try and see if it works.

Not work good.
rotate 180
2202921db3ed5c0a062ff4862cffe42
The same as rotate 0,so it's not correct.

rotate 90
dd8e09155e8f44d962c5b585b3230be

display.set_rotation(lv.DISPLAY_ROTATION._180)

btn1= lv.button(lv.screen_active())
btn1.center()
label=lv.label(btn1)
label.set_text("hello")

@kdschlosser
Copy link
Collaborator

give it another try.

@jd3096-mpy
Copy link
Author

jd3096-mpy commented Sep 9, 2024

give it another try.

AttributeError: 'lv_display_t' object has no attribute 'layer_bottom'
It shoule be get_layer_bottom

TypeError: 'Blob' object isn't subscriptable
scrn = self._disp_drv.screens[i]

I don't know how to deal with this,still some mistakes.

for i in range(self._disp_drv.screen_cnt):
        scrn = self._disp_drv.screens[i]
        
        scrn.update_layout()
        width = scrn.get_width()
        height = scrn.get_height()
        
        scrn.set_style_transform_pivot_x(int(width / 2), 0)
        scrn.set_style_transform_pivot_y(int(height / 2), 0)
        scrn.set_style_transform_rotation(rotation, 0)

I change to:

scrn = self._disp_drv.get_screen_active()
scrn.update_layout()
width = scrn.get_width()
height = scrn.get_height()
scrn.set_style_transform_pivot_x(int(width / 2), 0)
scrn.set_style_transform_pivot_y(int(height / 2), 0)
scrn.set_style_transform_rotation(rotation, 0)

for i in range(0,3):
    try:
        scrn = self._disp_drv.get_screen_prev()
        scrn.update_layout()
        width = scrn.get_width()
        height = scrn.get_height()
        scrn.set_style_transform_pivot_x(int(width / 2), 0)
        scrn.set_style_transform_pivot_y(int(height / 2), 0)
        scrn.set_style_transform_rotation(rotation, 0)
    except:
        pass

@jd3096-mpy
Copy link
Author

My screen is 640x480
I set to
_WIDTH = const(480)
_HEIGHT = const(480)
5096295286685f4eb4d32c3d97a9359
_WIDTH = const(640)
_HEIGHT = const(640)
ec9b22d333c110879484a4ecb9ee68e
_WIDTH = const(480)
_HEIGHT = const(640)
670c2c4d990ff283d116065697b62b1
_WIDTH = const(640)
_HEIGHT = const(480)
c31e0076bb9cd5538e4fe3f1b1d7b1e

What's next step to do?

@jd3096-mpy
Copy link
Author

I've decided to abandon using software methods to rotate the screen on the ESP32-S3. Even if successful, it would result in a very low frame rate. It seems that the ESP32-S3 is only suitable for RGB screens that don't require rotation. Thank you for your efforts during this time. Below, I'll share the MicroPython method I wrote for implementing 9-bit 3-wire SPI purely through pin control. I hope it can help those in need.

https://github.com/jd3096-mpy/micropython-3-wire-spi-soft/

@kdschlosser
Copy link
Collaborator

hey hey hey chill out there speedy.

I am working on some modifications to the driver that will utilize 2 full display frame buffers and a single partial frame buffer. The partial one is what LVGL would fill so when a small update gets made the entire display doesn't have to be redrawn. I am hammering out the code right now for the rotation which will do the rotation and the copy of the partial buffer to one of the full frame buffers. Once the copy is done I will then swap the 2 full frame buffers. If 2 partial updates happen before a full frame buffer is able to be sent that's ok because both partials can be written to the idle frame buffer with no problems.

To make this all work properly I have copied the RGB panel code form the ESP-IDF that I am stripping down and modifying to turn it into an actual bus driver instead of a panel driver. I am going to add the needed things that will allow me to keep all the buffers in sync.

This will greatly improve the performance when working with RGB displays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants