Xinyuan-LilyGO / T-Display-S3-AMOLED

An upgraded version of T-Display-S3. It has a high-resolution color screen and more configurable GPIO ports. Enrich your needs.
MIT License
143 stars 29 forks source link

RM67162 Datasheet #2

Closed nikthefix closed 1 month ago

nikthefix commented 1 year ago

Please add latest version of RM67162 datasheet.

The readily available V0.5 does not show support for QSPI and the colour correction registers detailed do not have any effect. Display dimming / brightness / invert / partial display registers all work as expected as of V0.5 of RM67162 datasheet.

nikthefix commented 1 year ago

Attached:

Modified RM67162 driver files which add:

  lcd_brightness(0xD0); // (working) 0-0xff (0xD0 is lilygo default)
  lcd_set_colour_enhance(0b00000xxx); // (not working) bit1:0=slr_level_0-2, bit2=slr_enable
  lcd_display_set_colour_enhance_low_byte(0bxxxxxxxx); // (not working)
  lcd_display_set_colour_enhance_high_byte(0bxxxxxxxx); // (not working)
  lcd_display_high_brightness_mode_on(); // (working) 
  lcd_display_high_brightness_mode_off(); // (working)      
  lcd_display_on(); // (working)
  lcd_display_off(); // (working)
  lcd_display_invert_on(); // (working)
  lcd_display_invert_off(); // (working)

nikthefix

RM67162_Modified.zip

davidos81 commented 9 months ago

hi and thanks for your brilliant effort! on the subject of modifications to this brilliant little device , I am struggling to use one of the other modes in particular the 8bit color mode , when i change the values according to the manual , all I get is garbage on screen, please look at the init code from your rm67162.cpp file , what values should I use on the highlighed line below to enable correctly the other (lower) bit depth color modes ?? Many thanks in advance !!

const static lcd_cmd_t rm67162_qspi_init[] = {
    {0x11, {0x00}, 0x80}, // Sleep Out
    // {0x44, {0x01, 0x66},        0x02}, //Set_Tear_Scanline
    // {0x35, {0x00},        0x00}, //TE ON
    // {0x34, {0x00},        0x00}, //TE OFF
    // {0x36, {0x00},        0x01}, //Scan Direction Control
*****************************************************************************************************
    {0x3A, {0x55}, 0x01}, // Interface Pixel Format 16bit/pixel  <<< THIS LINE
*****************************************************************************************************
    // {0x3A, {0x66},        0x01}, //Interface Pixel Format    18bit/pixel
    // {0x3A, {0x77},        0x01}, //Interface Pixel Format    24bit/pixel
    {0x51, {0x00}, 0x01}, // Write Display Brightness MAX_VAL=0XFF
    {0x29, {0x00}, 0x80}, // Display on
    {0x51, {0xD0}, 0x01}, // Write Display Brightness   MAX_VAL=0XFF
};
nikthefix commented 9 months ago

Hi, Page 90 of the datasheet shows the bit field settings to define interface pixel format (command 0x3A):

For the lower colour depth modes use following:

SPI 3 bit/pixel (8 colors); SPI 1-1-1 {0x3A, {0x71}, 0x01}, // Interface Pixel Format

SPI 8 bit/pixel (256 colors); SPI 3-3-2 {0x3A, {0x72}, 0x01}, // Interface Pixel Format

SPI 8 bit/pixel (256 colors); SPI 256 Gray {0x3A, {0x73}, 0x01}, // Interface Pixel Format

But you'll notice from the datasheet that these modes use SPI only so you may need to modify user code to accomodate spi byte ordering. Alternatively, if you just want an 8-bit colour depth to reduce buffer size etc then you could use the 16bit pixel format and only transfer the most significant byte of the 16bit word. Depending on your user code you may or may not have to pad the LSB with all zeros in order to complete a valid transaction. If using the lib as is here then you could place each 8bit data byte from your image array into a temp uint16_t varable and bitwise left shift x8 before calling 'push colours'.

Nikthefix

davidos81 commented 9 months ago

Many thanks for that , I exhausted all my options here before I posted my question , and yes to confirm , you are indeed correct with the spi , the 8bit modes ( using quad spi in the lib ) will produce a squashed picture , I kind of figured that you had to use the regular spi to get it perfect - But I was unsure (until you just confirmed it!) - but doing that you will decrease the bandwidth , so again , its a double edged sword. I ended up doing exactly as in your last sentence!

Many thanks for your kindness , cheers

nikthefix commented 9 months ago

My pleasure.

diegomacario commented 4 months ago

Hi @nikthefix! Sorry for commenting on this really old issue, but I also have a question related to the drivers of the T-Display S3 AMOLED V1.0

In this issue you can see the problem I'm running into: https://github.com/Xinyuan-LilyGO/LilyGo-AMOLED-Series/issues/21

Do you know if using any of these things can help me get rid of the tearing I'm seeing?

    // {0x44, {0x01, 0x66},        0x02}, //Set_Tear_Scanline
    // {0x35, {0x00},        0x00}, //TE ON
    // {0x34, {0x00},        0x00}, //TE OFF
    // {0x36, {0x00},        0x01}, //Scan Direction Control

I have read the datasheet, but I'm still not sure about what those things do or if they can help me. Thank you for any advice!

nikthefix commented 4 months ago

@diegomacario I've not experienced this myself. Try reducing the QSPI data rate a little bit. Do things improve? I always test using full screen animated GIFs which would show tearing most obviously. Are you using a full screen buffer? Are you accessing the PSRAM for other purposes at the same time? Did you base your code off an existing example or template? If so which one?

The init codes you mention won't help as the TE requires a hardware hookup from the display to the micro-controller to manage sync, which is not implemented in this board (looking at the schematic). But it should not be necessary if all transfers to the display GRAM are complete before any changes to the source buffer are made.

Edit: I stand corrected - the TE line IS available and connected to gpio 9.

nik

3Dsf-info commented 4 months ago

On 21 Feb 2024, at 15:36, nikthefix @.***> wrote:

I always test using animated GIFs which would show tearing most obviously. Are you using a full screen buffer? Are you accessing the PSRAM for other purposes at the same time?

Incidentally, what codebase do you use to play back animated GIFs? I was only able to do it using moononournation's Arduino_GFX.

nikthefix commented 4 months ago

@3Dsf-info Either LVGL integrated GIF decoder or for TFT_eSPI, I use Larry Bank's decoder: https://github.com/bitbank2/AnimatedGIF

His work is awesome!

moononournation's work is awesome too. If you got it to work with Arduino_GfX then, for all the other stuff it offers, it's amazing.

diegomacario commented 4 months ago

Hi @nikthefix! Thanks for your reply!

To give you some context: my project is a ray-tracer for the T-Display S3 AMOLED. In the little menu I showed you select the object you want to render, and then it starts rendering it and revealing it one pixel at a time.

The whole thing works beautifully, it's just the tearing in the menu that's annoying. My source code is here: https://github.com/diegomacario/Tiny-Ray-Tracer/tree/main/examples/Tiny_Ray_Tracer

And this is the source code of the menu: https://github.com/diegomacario/Tiny-Ray-Tracer/blob/main/examples/Tiny_Ray_Tracer/MenuState.cpp

The way I wrote it, each box on the screen has its own sprite. When the button to jump to the next box is pressed, I update the colors of the current sprite and the next one:

   bool nextSceneButtonIsPressed = checkNextSceneButton();
   if (nextSceneButtonIsPressed) {
      // Make the current cell black with white text
      mCellSprites[mCurrentCellIndex].fillSprite(TFT_BLACK);
      mCellSprites[mCurrentCellIndex].setTextColor(TFT_WHITE);
      mCellSprites[mCurrentCellIndex].drawRect(0, 0, static_cast<int32_t>(mCellWidth), static_cast<int32_t>(mCellHeight), TFT_WHITE);
      mCellSprites[mCurrentCellIndex].drawString(mCells[mCurrentCellIndex].mText.c_str(), mCells[mCurrentCellIndex].mTextXPos, mCells[mCurrentCellIndex].mTextYPos);

      mAmoled->pushColors(mCells[mCurrentCellIndex].mXPos, mCells[mCurrentCellIndex].mYPos, static_cast<uint16_t>(mCellWidth), static_cast<uint16_t>(mCellHeight), (uint16_t *)mCellSprites[mCurrentCellIndex].getPointer());

      ++mCurrentCellIndex;
      mCurrentCellIndex %= mCells.size();

      // Make the selected cell white with black text
      mCellSprites[mCurrentCellIndex].fillSprite(TFT_WHITE);
      mCellSprites[mCurrentCellIndex].setTextColor(TFT_BLACK);
      mCellSprites[mCurrentCellIndex].drawRect(0, 0, static_cast<int32_t>(mCellWidth), static_cast<int32_t>(mCellHeight), TFT_WHITE);
      mCellSprites[mCurrentCellIndex].drawString(mCells[mCurrentCellIndex].mText.c_str(), mCells[mCurrentCellIndex].mTextXPos, mCells[mCurrentCellIndex].mTextYPos);

      mAmoled->pushColors(mCells[mCurrentCellIndex].mXPos, mCells[mCurrentCellIndex].mYPos, static_cast<uint16_t>(mCellWidth), static_cast<uint16_t>(mCellHeight), (uint16_t *)mCellSprites[mCurrentCellIndex].getPointer());
   }

So I only update the parts of the screen that change. To answer your other questions:

The curious thing is that I only see tearing when I drastically change the color of something I drew from one frame to the next (like I do with my buttons that go from black to white and vice versa).

I tested animating a sphere moving from left to right in the screen, and I see no tearing.

It's only when the color changes so much like in my case.

And do you mind if I ask you, how do I lower the QSPI data rate? I'm not sure how to do that.

diegomacario commented 4 months ago

Note that in my project I'm using the LilyGo_Class from #include <LilyGo_AMOLED.h> to push colors to the screen, but I have also tested using your RM67162 driver code and I see the same problem.

davidos81 commented 4 months ago

you might need to vsync , but i am not sure if that pin is connected to the esp32on this board if it is , just wait for vsync then do your drawing stuff but i think you may need to go low level and write your own push colors routine as the ones provided in libraries are not optimised for performance but for compatibility....

nikthefix commented 4 months ago

@diegomacario

Interesting. I'll have to think about that. To adjust the data rate depends on which library you're using. If it's my mod of the original Lilygo Lib then you can change it in pins.h

So the animated sphere shows no tearing. What if you animate it vertically rather than horizontally and / or change it's colour?

nikthefix commented 4 months ago

@davidos81 There's no vsync here. Just a transfer of data to the amoled internal graphics ram buffer. Addressing is arbitrary so as long as your data source for a complete frame or parts of it is consistent there should be no tearing - unlike the RGB displays which are a constant pain with the ESP32-S3 in my experience.

davidos81 commented 4 months ago

but there must be a way to remove tearing - surely ?

nikthefix commented 4 months ago

@davidos81 There shouldn't be tearing if the buffer representing what is to be drawn is not modified in any way before it's contents are fully transferred to the GRAM of the display. Modifications of the ESP buffer should be blocked by the writing to GRAM until completion. If however, the tearing is caused by the display not keeping pace then a reduction in the QSPI data rate would give us a clue. The 75Mhz of the original Lilygo setup seems high-ish but has worked consistently for me.

diegomacario commented 4 months ago

@nikthefix I tried lowering this value:

#define SPI_FREQUENCY         75000000

I went down all the way down to 5Mhz and I still observed tearing. Now I'm using this tiny code to reproduce the bug without any additional complexity:

#include "rm67162.h"
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();
TFT_eSprite img = TFT_eSprite(&tft);

unsigned int colorIndex = 0;
unsigned long frameCounter = 0;

void setup() {
  img.createSprite(100, 100);
  img.setSwapBytes(1);
  rm67162_init();
  lcd_setRotation(1);
}

void loop() {
  if (colorIndex == 0) {
    img.fillRect(0, 0, 100, 100, TFT_RED);
  } else if (colorIndex == 1) {
    img.fillRect(0, 0, 100, 100, TFT_GREEN);
  } else if (colorIndex == 2) {
    img.fillRect(0, 0, 100, 100, TFT_BLUE);
  }

  lcd_PushColors(268 - 50, 120 - 50, 100, 100, (uint16_t*)(img.getPointer()));

  frameCounter += 1;
  if (frameCounter % 720 == 0) {
    colorIndex += 1;
    colorIndex %= 3;
  }
}

It just draws a rectangle that cycles through red, green and blue periodically.

davidos81 commented 4 months ago

@davidos81 There's no vsync here. Just a transfer of data to the amoled internal graphics ram buffer. Addressing is arbitrary so as long as your data source for a complete frame or parts of it is consistent there should be no tearing - unlike the RGB displays which are a constant pain with the ESP32-S3 in my experience.

ok , my bad , its not called vsync it the "tearing" pin , this board does have one as I have used it , it can reduce tearing under certain circumstances - however you can also set the scanline direction from portrait to landscape , but I had no luck in reducing tearing in landscape - which is what is wanted here

nikthefix commented 4 months ago

@davidos81 Yes I stand corrected - the TE line IS available and connected to gpio 9.

@diegomacario You need to create one full screen sprite - img.createSprite(240, 536); or img.createSprite(536, 240); for landscape. This sprite then acts as your internal frame buffer which you draw into with the usual tft_espi functions. The lcd_pushColours() function pushes a whole screen of data at once. Your 100x100 sprite represents only a partial screen update. The lcd_pushColours() function expects each line to be full width so a 100x100 region will be incoherent and lead to all sorts of artefacts.

davidos81 commented 4 months ago

@davidos81 Yes I stand corrected - the TE line IS available and connected to gpio 9.

@diegomacario You need to create one full screen sprite - img.createSprite(240, 536); or img.createSprite(536, 240); for landscape. This sprite then acts as your internal frame buffer which you draw into with the usual tft_espi functions. The lcd_pushColours() function pushes a whole screen of data at once. Your 100x100 sprite represents only a partial screen update. The lcd_pushColours() function expects each line to be full width so a 100x100 region will be incoherent and lead to all sorts of artefacts.

Thanks for clarifying that! So diegomacario - you now have your solution thanks to nikthefix! good luck with it!

nikthefix commented 4 months ago

@diegomacario @davidos81

Things can be a little confusing with the T-DisplayS3-AMOLED at present as there are 3 variants of the board and 3 different libraries which users may have been using initially:

Boards - T-DisplayS3-AMOLED, T-DisplayS3-AMOLED-Version 2 (shorter), T-DisplayS3-AMOLED-Version 2 with touch. The version 2 boards have a display enable on gpio 38 which needs to be pulled high to activate the screen. The original V1 boards does not. This means that user code for V1 may not work with V2 unless this is added to the sketch.

Libraries-

  1. T-DisplayS3-AMOLED original repo by mmMicky (https://github.com/Xinyuan-LilyGO/T-Display-S3-AMOLED)
  2. My modified version which was used in Volos' first T-DisplayS3-Amoled videos (code available on his github)
  3. The new consolidated Lilygo Amoled repo (https://github.com/Xinyuan-LilyGO/LilyGo-AMOLED-Series)

I would say that option 3 is a little unwieldy for those wishing to extensively modify and tailor / optimize the code as it attempts to be universal across the amoled range.

Options 1&2 are small and it's easier to see what's going on - assuming you're just using this one board.

Added to the fact that ESP_Arduino Alpha3 is still teething, it means that the space is a bit messy right now. LVGL V9 will no doubt throw a syntactical spanner in the works too! But the migration documents for both ESP_Arduino and LVGL are superb.

Nick

nikthefix commented 4 months ago

@diegomacario @davidos81 Just another point to add: If you find that a full screen update for animation is too slow then you CAN use lcd_push_colours() for partial update but there is a caveat. See page 68 and 70 of the RM67162 datasheet.

For both Row and Column Start/End (RASET and CASET registers):

(1) SC[9:0] always must be equal to or less than EC[9:0]. (2) The SC[9:0] and EC[9:0]-SC[9:0]+1 must be divisible by 2.

Failure to 'round off' your update regions to meet these criteria will result in the kind of intermittent 45 degree skewing that looks like tearing but is in fact a restriction of the display hardware. Your screen shots look to me like this is what was happening. A full screen update avoids the issue as 536x240 meets these criteria.

LVGL has a 'rounder' callback function which does this automatically if needed for displays with fussy even / odd quantized partial update restrictions. You could port this function to your TFT_eSPI project or roll your own. It's on my todo list to add fast partial updates to my RM67162 driver.

I'll have updates on my github (https://github.com/nikthefix) just as soon as I get my electronics workroom functional again. I had a water leak so instead of doing fun stuff I've been re-plastering and dehumidifying! I also need to demystify my workroom as it was a mess - like my brain.

diegomacario commented 4 months ago

Wow thank you so much @nikthefix! I learned so much reading this thread. You are totally right about this space being a little confusing right now with multiple driver options and board versions.

I'll report back here if I'm able to make the tearing go away with the suggested approaches. Thank you!

nikthefix commented 4 months ago

@diegomacario No problem. I also learn a hell of a lot from these issues and interactions. But just for the record, I think that what you were seeing was not tearing. The photos showed none of the characteristics of tearing. Try the full screen sprite first and also - lcd_PushColors(0, 0, 536, 240, (uint16_t *)spr.getPointer()); If it works without artifacts / corruption then we know that the above display restrictions are pertinent. I get around 20-30 fps with full screen update @ 75MHz but it depends on all the other business code. Once you're satisfied that everything is rendering cleanly then the next step could be to optimize for speed if that's what you need.

nik

3Dsf-info commented 4 months ago

On 21 Feb 2024, at 15:59, nikthefix @.***> wrote:

Either LVGL integrated GIF decoder or for TFT_eSPI, I use Larry Bank's decoder: https://github.com/bitbank2/AnimatedGIF

His work is awesome!

Thanks!

That’s interesting - I couldn’t get the Bank code to compile at all, and the LVGL stuff never worked for me. Adding to the nuisance is having to convert the GIF into .h every single time!

I don’t suppose you’d have a writeup somewhere on how to do this? I was able to use Arduino_GFX, but only by using the older version of Arduino IDE since the newer versions can’t use plugins for file uploads. So messy!

nikthefix commented 4 months ago

@3Dsf-info Ah so were you hoping to use SPIFFS / littleFS / FatFS to store your Gifs? Yeah the new IDE won't play ball for upload at present. But it's a good thing really. Converting your file to a byte array and including it in the sketch is so much more efficient. But for experimentation having a file system is convenient. Especially for web pages etc. You could upload using the espressif flash tools or try implementing your board as a USB Mass Storage Device. But I'd stick with the .h file. Harder work but better in the end. And the extra hoops you have to jump through pay dividends at the final cut.

"The LVGL stuff never worked for me". Can you expand on this?

The included LVGL Gif examples are rudimentary but should work out-of-the-box if lv_conf.h has been modified to enable this functionality.

davidos81 commented 4 months ago

@diegomacario No problem. I also learn a hell of a lot from these issues and interactions. But just for the record, I think that what you were seeing was not tearing. The photos showed none of the characteristics of tearing. Try the full screen sprite first and also - lcd_PushColors(0, 0, 536, 240, (uint16_t *)spr.getPointer()); If it works without artifacts / corruption then we know that the above display restrictions are pertinent. I get around 20-30 fps with full screen update @ 75MHz but it depends on all the other business code. Once you're satisfied that everything is rendering cleanly then the next step could be to optimize for speed if that's what you need.

nik

i had the same issue (that tearing in video) in landscape mode , whats happening (i think) is that the lcd ALWAYS draws in portrait mode , the rotation stuff is probably for convenience like in most lcd controller ic's , so no matter what I did ( and i spent days!) i could not get it to work

nikthefix commented 4 months ago

@diegomacario No that's wrong, This is definitely true of the Long and the T-Track, but the display of the T-Display-Amoled has a true hardware rotate. When in landscape mode it can draw landscape and there's no penalty (the scan lines are able to rotate). This is also true of the T4-amoled. If you have issues then the causes are elsewhere.

davidos81 commented 4 months ago

hmmm , interesting! As i always got a diagonal tear no matter what I did in lansdscape , so what you are saying is if I set rotation to landscape - then wait for the tear pin then push a full screen bitmap of 536x240 - that should be tear free - right ?

nikthefix commented 4 months ago

Or just measure the duration of a full screen landscape push and block all writes within this window. But I think there's something else going on here. I've stressed this board and I've never seen anything like tearing. Apart from the artifacts mentioned above which were due to not adhering to the recommendations of the datasheet.

davidos81 commented 4 months ago

I prefer a visual approach , what i do is change the colors on each "frame" , a tear is very easy to spot - you will see a visible "line" between the time you changed from the old to the new color.

davidos81 commented 4 months ago

if you dont see a line then grab yourself a cigar ...well done!

BTW smoking is bad for health ( just a joke!)

nikthefix commented 4 months ago

Smoking what? There's health and there's health.

davidos81 commented 4 months ago

Smoking what? There's health and there's health.

LOL!

nikthefix commented 4 months ago

@davidos81 Post your code. Make sure it's a full screen sprite and that the framework is Alpha3 and the latest TFT_eSPI. I'll smoke that and get back to you!

Preferably something which dramatically demonstrates your issue.

davidos81 commented 4 months ago

cheers for that nikthefix , but i haven't messed with this board in a while , i only replied so I could help the dude with tear issues... I simply gave my opinion on my findings hopefully to help those here But when i get a moment I will try the suggestions from yourself and others!

nikthefix commented 4 months ago

@davidos81 Nice one! It all helps.

diegomacario commented 4 months ago

Hi @nikthefix! Sorry for the super late follow up, I got busy with work.

Sadly using a fullscreen lcd_PushColors of 536x240 that respects the rules of the datasheet doesn't fix the issue. I still see the same odd diagonal tearing with this code:

#include "rm67162.h"
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();
TFT_eSprite img = TFT_eSprite(&tft);

unsigned int colorIndex = 0;
unsigned long frameCounter = 0;

void setup() {
  img.createSprite(536, 240);
  rm67162_init();
  lcd_setRotation(1);
}

void loop() {
  if (colorIndex == 0) {
    img.fillRect(0, 0, 536, 240, TFT_RED);
  } else if (colorIndex == 1) {
    img.fillRect(0, 0, 536, 240, TFT_GREEN);
  } else if (colorIndex == 2) {
    img.fillRect(0, 0, 536, 240, TFT_BLUE);
  }

  lcd_PushColors(0, 0, 536, 240, (uint16_t*)(img.getPointer()));

  frameCounter += 1;
  if (frameCounter % 180 == 0) {
    colorIndex += 1;
    colorIndex %= 3;
  }
}

It's very curious, I'll keep thinking about this problem.

nikthefix commented 4 months ago

@diegomacario

OK, I will try to reproduce the problem using your example. nik

davidos81 commented 4 months ago

@nikthefix

i just tried this and the experience I had a while back is exactly the same , my guess is that its still drawing in portrait mode (physically on the LCD) but the hardware is drawing it rotated so to you and me its landscape ? Make sense to u ? I spent a good few days trying to figure it out but no avail - My solution back then was just use portrait mode with my gfx rotated !

nikthefix commented 4 months ago

@davidos81

So you tried this using diegomacario's code and see the same problem he describes? I've not had a chance to try it yet as my work room is still down but I'll try to set up a test rig today and get back to you asap.

Yes rotating the GFX is something I tried with other more limited displays in the past. The trouble with that is that it can mess with your fonts / smooth fonts - depending on how you're rendering them.

davidos81 commented 4 months ago

@davidos81

So you tried this using diegomacario's code and see the same problem he describes? I've not had a chance to try it yet as my work room is still down but I'll try to set up a test rig today and get back to you asap.

thats correct!

Yes rotating the GFX is something I tried with other more limited displays in the past. The trouble with that is that it can mess with your fonts / smooth fonts - depending on how you're rendering them.

my use case was just bitmaps and didnt need general purpose gfx so i did not use any font related stuff...

nikthefix commented 4 months ago

@diegomacario @davidos81

Morning Gentlemen, sorry for the delay in getting back. Could you try the following sketch - even more simplified.

In pins_config.h set #define SPI_FREQUENCY 500000

Please confirm that you see no tearing at this very low spi speed and that the scanning direction / orientation looks correct.

include "rm67162.h"

include

TFT_eSPI tft = TFT_eSPI(); TFT_eSprite img = TFT_eSprite(&tft);

unsigned int colorIndex = 0; unsigned long frameCounter = 0;

void setup() { //pinMode(38, OUTPUT); //for s3 amoled touch //digitalWrite(38, HIGH); //display enable

img.createSprite(536, 240); rm67162_init(); lcd_setRotation(1); }

void loop() {

img.fillRect(0, 0, 536, 240, TFT_RED);
lcd_PushColors(0, 0, 536, 240, (uint16_t*)img.getPointer());
delay(1000);

img.fillRect(0, 0, 536, 240, TFT_GREEN);
lcd_PushColors(0, 0, 536, 240, (uint16_t*)img.getPointer());
delay(1000);

img.fillRect(0, 0, 536, 240, TFT_BLUE);
lcd_PushColors(0, 0, 536, 240, (uint16_t*)img.getPointer());
delay(1000);

}

nikthefix commented 4 months ago

@diegomacario @davidos81

I am also seeing the effect now when using fillRect. I think the reason I didn't see it before was because I was only pushing full screen bitmap images - and for some reason (which may help point to the problem) - the effect doesn't show up in this case. I'm sure it can be resolved but might take some more experimentation. I'll try the same basic screen washes in LVGL to see if it's the same. The typical 'tearing' in which the MCU frame buffer is updated during an update phase of the internal display buffer wouldn't seem to apply here as in this example we can be sure that the MCU buffer is constant before, during and after the call to lcd_pushColors. As davidos81 suggested, we might be able to make use of the TE line (per line of the display). And there are other options shown in the datasheet which may help.

davidos81 commented 4 months ago

@nikthefix "Please confirm that you see no tearing at this very low spi speed and that the scanning direction / orientation looks correct."

confirmed... but with all due respect - it does not solve the issue or does it ??

nikthefix commented 4 months ago

@davidos81 No I just wanted to make sure we were all seeing the same thing as a starting point - just in case there were differences in our configurations. Sorry, I should have been clear that it wasn't meant as a solution.

I'm just looking through the display datasheet at the moment to see if there's a way to defer update of the display until the display internal GRAM has been fully populated with a full frame of data. I believe this is the cause of the problem as the display appears to be updating from its own internal buffer in an asynchronous fashion.

I've tried everything I can think of in the esp spi driver code to no avail. Even setting the spi transfer buffer to a single 536 px line has the same problem at speed. So unless there's a bug in the esp qspi hal when operating at high data rates, I recon the problem will be found and hopefully solved by looking at the display itself and using the TE line with an interrupt on the ESP32 - just as you suggested in the first place :)

nik

nikthefix commented 4 months ago

@davidos81 @diegomacario

Progress: It's working without tearing now by using the TE line to detect the blanking periods during which you can transfer data without the display updating from internal buffers. There's a speed penalty as the 'write window' is now much smaller (still at 75MHz tho). I'm just polling the TE line at the moment but will convert to an interrupt flag which should give us back some speed. It looks clean now at least.

nik

davidos81 commented 4 months ago

@nikthefix you are a star!!

I personally didn't think it was possible , even with my experience in this kind of stuff , my guess was (up until now) like most lcd boards , there is no sync between writing to gdram and lcd displaying it from there , I did manage it in portrait though but not in landscape - like you have done here...!

BTW , Is the plastering finished ..?

diegomacario commented 4 months ago

That's incredible @nikthefix thanks for keeping up the fight to solve this problem!!!

nikthefix commented 4 months ago

@diegomacario @davidos81

Here's a zip with a sketch and modified libs. At the moment the frame data are transmitted during the display blanking phases only so it's slower than we want. The next step is to extend this into the 'danger zone' but keeping sync with the display frame rate (approx. 60.5Hz). Just need to measure the exact timings. And yes it seems that the timings have to be considered as 'portrait mode' timings even though the display supports a logical landscape orientation for data transfer. Confusing. You can see in the init codes that my TE scan line assert is at line 535 (n-1) and not 239 as I was half expecting.

Current shortcomings: At present the spi buffer contains 1/8 of the whole frame which is a restriction of the esp32-s3 max spi buffer size. Each 8th of a frame is transmitted per assert of TE (which is at 60.5Hz). Combined with the fact that TE is tested twice to find a reliable rising edge for sync, we are well short of potential. If all goes well we should expect a tear free frame rate 16 times that of the attached sketch - fingers crossed.

@davidos81 Yes plastering done! But of course, after all such household emergency work, you automatically start thinking about how you could re-model the space. At least I'm up and running again.

nik

t3_amoled_tear_test.zip