TFT Driver ST7789 Works on H7 not on RT1062

I’m porting code from an Cam H7 R2 FW 4.3.3 to RT1062 FW 4.5.9.

My code to initialze and write 320x240 grayscale images to a TFT display with ST7789 driver works on the H7 FW 4.3.3, while on the RT1062 FW 4.5.9 the images on the display are jumbled up. Attached is my code that recreates the issue. Thanks in advance for your help.

Test_TFT_ST7789_V1.py (2.1 KB)
TFT_ST7789VI.py (4.1 KB)

Hi, the SPI display driver was explicity extended to resolve the need for anyone to have to write their own display driver for any SPI bus displays.

class SPIDisplay – SPI Display Driver — MicroPython 1.23 documentation

We added a bus_write() method which lets you execute commands to setup the display. Using this, you can send whatever custom initialization commands you need. Otherwise, you can tell the SPI display module the width/height, byte swap, and bgr required.

The default setup otherwise is very minimal: openmv/src/omv/modules/py_spi_display.c at master · openmv/openmv

Whatever the case, it should be much faster than having to manually byteswap data in python like I see you are doing in your module.

while on the RT1062 FW 4.5.9 the images on the display are jumbled

Can’t really debug this code for you. But, the RT1062 has a different maximum SPI bus transfer length than the STM32H7. I had to manually patch the H7 micropython driver to allow it to write more than 65KB at a time in one spi transaction. The RT1062 can’t do more than 32KB. The upstream MicroPython SPI bus driver for this doesn’t look to be patched… so, you’ll need to breakup the transaction into chunks less than this:

#define OMV_SPI_MAX_8BIT_XFER   (32768U - 32U)
#define OMV_SPI_MAX_16BIT_XFER  (32768U - 16U)

Is there a maximum image size for SPIDisplay.write? Will it write a 320x240 RGB image?

How do I break the image into smaller chunks for SPI transmission?

Also, same error as in other thread … AttributeError: ‘module’ object has no attribute ‘SPIDisplay’

Something very weird is going on for it not to work. I can install the latest dev release and the firmware posted in the other thread and it works.

The no attribute ‘SPIDisplay’ error resolved itself after erasing the SD card, rebooting computer and reloading firmware onto RT1062. The lcd_1.py example is working now with the LED Shield.

I’m looking to implement a 320x240 TFT display with the ST7789 driver on the RT1062. How do I break the image into smaller chunks in python for SPI transmission to this display?

Use the bytearray() method on the image and then a memory view to access parts of the image.

However, this will be very slow if you need to byte reverse things. I recommend just using our SPI display module and using the bus_write method to send the commands to setup the display as required.

Thanks for your help. I’m a bit confused by your eariler reply regarding chunking the image.

Will the SPIDisplay module work with a 320x240 TFT display on the RT1062 with this?

sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)  
lcd = display.SPIDisplay(width = 240, height = 320)
while True:
    lcd.write(sensor.snapshot())

Also, what are the options for “controller field”. I don’t see this in the documentation.
“controller Pass the controller chip class here to initialize it along with the display.”

Yes, it’s that easy!

As for the controller. It would look something like this:

openmv/scripts/libraries/st7701.py at master · openmv/openmv

You’d just pass whatever class you make as the controller argument. In the above case:

display.DSIDisplay(controller=ST7701())

Then, the controller class just is just:

import time

class ST7701:
    def __init__(self):
        pass

    def init(self, dc):
        self.dc = dc
        dc.bus_write(REG, b"<BYTES_TO_WRITE>")

This allows you to fully customize setting up the display. Note that the dc object passed is the display object itself. So, you can query it for whatever info you think you need.

Note that there’s no register reading implemented for SPI displays. So, you can just write settings.

bus_write() does the command byte write followed by a bytestring for the argument for that command.

We implemented this system so that folks could use custom displays while still achieving high performance with triple buffering and etc. from our driver.

Great! One last question before I implement, what are the display driver chip connections for each of the “Uses pins P0, P2, P3, P6, P7, and P8”?

See the LCD shield pinout: LCD Shield – OpenMV

Put it all together and it works great at 18-19 FPS. My display is rotated 90 deg to sensor and IDE. WIthout the rotate image, it runs at 20-21 FPS. There may be a way with ST77XX_CASET and ST77XX_RASET to adjust for this without having to rotate the image in the framebuffer.

Here’s my code for a 320x240 TFT with the ST7789 Driver on the RT1062.

Test_TFT_ST7789_V2.py (2.0 KB)

You can rotate the image during the display.write() call. This will be faster than doing it on the image directly as this avoids a write back to SRAM of the rotated image.

We’ve been slowly adding the draw_image() GPU backend to functions. Please take advantage of it as it will save you writebacks, which cost speed.

Generally, you should avoid transposing an image except for writing it to a final display buffer as the operation is really hard on the CPU. This is kinda why there’s no transpose() unary op. As it should not be something you do.

Great to know. Increased 1 FPS using lcd.write(img, hint=image.ROTATE_90). How is the clock rate for the SPI determined from refresh. It seems to max out with refresh=45?

The ST7789 does not require the P7 RESET line and works without it. Can P7 be reassigned after display.SPIDisplay without breaking display.write?

The clock is just widthheight2*refresh.

The pins are hardcoded, only P6 can be freed. Otherwise, you need to modify the firmware.

It looks to me like P7 OMV_SPI_DISPLAY_RST_PIN is only toggled in mp_obj_t spi_display_make_new. Is it used anywhere else?

I did the following quick test and saw nothing abnormal on the TFT display.

lcd = display.SPIDisplay(width = 240, height = 320, refresh = 45, bgr = False, byte_swap = False, controller=ST7789())
pin7 = Pin(“P7”, Pin.OUT, Pin.PULL_UP)

while True:
img = sensor.snapshot()
lcd.write(img, hint=image.ROTATE_90)
pin7.toggle()

Yeah, it’s used just for that to reset the display.