CoretechR / OMOTE

Open Source Remote Using ESP32 and LVGL
https://hackaday.io/project/191752
GNU General Public License v3.0
1.02k stars 113 forks source link

Intermittent LCD initialization failure #70

Closed JustMe-NL closed 2 months ago

JustMe-NL commented 3 months ago

When the rev.3 hardware is run from battery the remote sometimes (probably) does not initialize the LCD correctly. The result is that the backlight is lit but the screen is blank. The user-led pulses its heartbeat and the buttons do work correctly. Usually I can let the remote rest until the screen shuts off and the ESP32 goes to sleep. After that usually the screen functions normal again.

Does anybody else experience this phenomenon and what could be the resolution? Change the start delay, add some capacitors or something else?

The LCD is an Adafruit original and run at the default 40MHz.

KlausMu commented 3 months ago

Yes, about every 50th or 100th restart I have the same. I already had a discussion with @CoretechR about it.

If it happens more often to you, you could try to play around with this code in main branch https://github.com/CoretechR/OMOTE/blob/ee68361273f4d0262138b418d8d783899c708520/Platformio/src/hardware/tft.cpp#L42

or this code in lvgl_simulator feature branch https://github.com/CoretechR/OMOTE/blob/51a6c7306c4c27dcf86ad88e97a793eef4b950d0/Platformio/hardware/ESP32/tft_hal_esp32.cpp#L54

In hardware rev 1 the code was necessary to prevent the ESP32 from brownout when powering the TFT chip.

Now, the problem is not the ESP32 brownout anymore, but it seems that the TFT chip sometimes does not boot correctly. You could

The last statement always has to be digitalWrite(LCD_EN_GPIO, LOW);, which powers the TFT chip.

@CoretechR any other ideas on how it could be solved in software?

CoretechR commented 3 months ago

@JustMe-NL Can you say how often this roughly happens? Does it only happen when the device is battery powered? Intermittent problems like this are always tricky to debug. It would definitely be interesting to see if the software workaround for Rev 1 has any effect on this. (The code changes how quickly the supply voltage for the display rises)

At the moment I am unable to reproduce this issue with my remote by just waking it up from standby. I have to manually toggle the reset pin of the display to get a static white image.

KlausMu commented 3 months ago

To me it also happens when it is USB powered, without battery. I don't know if removing the loop code for rev2 and rev3 helped. The code is in there only for some days. I didn't had this effect since then, but time is to short to say if it solved the issue.

JustMe-NL commented 3 months ago

As far as I can recall I have only seen this when on battery. At first I thought I had some sort of bad battery with not enough capacity or a large internal resistance to provide the needed current. But I saw the behavior on two different batteries.

Resetting only the ESP32 does not resolve this by the way, both devices need to reset to resolve the issue which thankfully always happens after a couple of seconds of sleep.

If I have some time I'll add some wires and check the voltagecurves on the scope.

CoretechR commented 3 months ago

OK, if you could check 3.3V and VSW with a scope, that would be interesting.

JustMe-NL commented 3 months ago

So as is usual with Murphy, once you connect the scope nothing goes wrong.

The setup: Gnd & 3V3 taken from C7/C8, VSW taken from the pin on Q4. Channel 1 (yellow): VSW @ 1V/div Channel 2 (blue): 3V3 @ 1V/div All measurements done on battery

But I did see something unusual. First I thought I forgot to clean & build to remove the startdelay from rev1 with regard to the slow rampup of VSW in the tft.cpp. But that was wrong, I did clean and rebuild but the scope still showed the following traces.

Late enable

How come there is a two-tier curve on VSW if the sloping function is disabled? Well as it turns out, the timing you see is proportional to the time it takes to setup the PWM channel. At the start of tft.cpp we initialize the GPIO pins (and turn them "off"), if we initialize the VSW GPIO to On before the channel setup we see the following traces:

Early enable

As of yet I'm not sure if this is the smoking gun and the definitive solution, but it is a software fix so you could easily try it out and observe if this eliminates the white screen problem. Furthermore, maybe this also eliminates the need for the delay(100) so the remote is just that fraction of a second faster to respond?

CoretechR commented 3 months ago

Thank you very much for your findings, @JustMe-NL! I don't remember seeing this behavior before. The ESP32 needed a lot of convincing to keep the LCD_EN and LCD_BL pins in a high state during sleep and to correctly handle the wakeup. But I always thought it would start cleanly like in you second screenshot. This can definitely have an effect on display not initializing correctly.

KlausMu commented 3 months ago

Maybe it is worth checking that everything is still in correct order with the new modulare approach. When everything was in one single file main.cpp, things from sleep and tft were intertwingled a lot. I broke them up in separate modules. Now we have init_tft() and init_sleep() at setup-time. And enterSleep() when going to sleep at runtime.

In the first version of main.cpp, code fom init_tft() and init_sleep() was mixed.

  --> this is now in init_tft()
  pinMode(LCD_EN, OUTPUT);
  digitalWrite(LCD_EN, HIGH);

  --> this is now in init_sleep()
  gpio_hold_dis((gpio_num_t)LCD_EN);

  --> this is now in init_tft()
  ledc_channel_config_t ledc_channel_left;

Now, first init_sleep() is called, afterwards init_tft()

CoretechR commented 3 months ago

Yes, I also noticed that the backlight seems to turn on for a split second before the PWM takes over. Maybe that happens for the same reason.

JustMe-NL commented 3 months ago

I saw the backlight flash as well today. Maybe it has something to do with the setup. Q3 & Q4 have gate threshold voltages of -0.4 to -1.2. But the ESP32 can have a Vout high as low as 0.8 x Vdd which is Vdd - 0,66 @ 3V3. Enough to maybe partially conduct Q3 or Q4, this could explain the weird curve: in the HIGH state, Q4 partially conducts (I did not measure Vgs though).

A solution could then be to keep the outputs as input (high-z) until we are ready to use them, you can set the outputlevel before the GPIO is defined as output (quoting https://www.esp32.com/viewtopic.php?t=28677).

CoretechR commented 3 months ago

I soldered wires to 3.3V, VSW, LCD_EN and LCD_BL and recorded the startup. This is what it looks like with the old (single main file) code: old code 2

And this is with the new modular code: modular code 2

Something does not seem quite right with the initialization. And this would at least explain the short backlight flickering.

KlausMu commented 3 months ago

Ok, I think I understand what is going on. I wanted the IMU to be initialized before TFT. Both are using I2C. To init IMU, the TFT needs to be powered, because otherwise the I2C bus is disturbed by the non powered tft. So I added

void init_hardware_general_HAL(void) {
  // For I2C to work correctly, the tft has to be powered.
  // Otherwise the IMU cannot be initialized.
  // The tft touch controller, being on the same I2C bus, seems to disturb if not powered.
  pinMode(LCD_EN, OUTPUT);
  digitalWrite(LCD_EN, LOW);                         <-- this is different

  // SDA and SCL need to be set explicitly, because for IMU you cannot set it explicitly in the constructor.
  // Configure i2c pins and set frequency to 400kHz
  Wire.begin(SDA_GPIO, SCL_GPIO, 400000);

at the very beginning of setup

As a test, could you please

  1. in main.cpp, deactivate "init_hardware_general();"

  2. in "hardware/ESP32/tft_hal_esp32.cpp", put add

    Wire.begin(19, 22, 400000);

    before

    touch.begin(128); // Initialize touchscreen and set sensitivity threshold
  3. in main.cpp move

    init_IMU();

    after

    init_gui();

And then please check with the scope if everything is as it was before. And if the flickering is gone.

KlausMu commented 3 months ago

And overall, here a comparison of the relevant old and new code, if of interest.

old code

void setup() {  

  // LCD Pin Definition
  pinMode(LCD_EN, OUTPUT);
  digitalWrite(LCD_EN, HIGH);
  pinMode(LCD_BL, OUTPUT);
  digitalWrite(LCD_BL, HIGH);

  gpio_hold_dis((gpio_num_t)LCD_EN);
  gpio_hold_dis((gpio_num_t)LCD_BL);
  gpio_deep_sleep_hold_dis();

  // Configure the backlight PWM
  ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL;
  ....

  // Slowly charge the VSW voltage to prevent a brownout
  // Workaround for hardware rev 1!
  for(int i = 0; i < 100; i++){
    digitalWrite(LCD_EN, HIGH);  // LCD Logic off
    delayMicroseconds(1);
    digitalWrite(LCD_EN, LOW);  // LCD Logic on
  }  

  Wire.begin(SDA, SCL, 400000); // Configure i2c pins and set frequency to 400kHz
  touch.begin(128); // Initialize touchscreen and set sensitivity threshold

  // Setup IMU
  IMU.begin();  
}

new code (without the suggested changes in the previous post)

void init_hardware_general_HAL(void) {
  // For I2C to work correctly, the tft has to be powered.
  // Otherwise the IMU cannot be initialized.
  // The tft touch controller, being on the same I2C bus, seems to disturb if not powered.
  pinMode(LCD_EN_GPIO, OUTPUT);
  digitalWrite(LCD_EN_GPIO, LOW);

  // SDA and SCL need to be set explicitly, because for IMU you cannot set it explicitly in the constructor.
  // Configure i2c pins and set frequency to 400kHz
  Wire.begin(SDA_GPIO, SCL_GPIO, 400000);

}

void init_sleep_HAL() {
  gpio_hold_dis((gpio_num_t)LCD_EN);
  gpio_hold_dis((gpio_num_t)LCD_BL);
  gpio_deep_sleep_hold_dis();
}

void init_IMU_HAL(void) {
  IMU.begin();  
}

void init_tft(void) {
  // this is power for the TFT IC
  pinMode(LCD_EN, OUTPUT);
  digitalWrite(LCD_EN, HIGH);
  // this is power for backlight LEDs
  pinMode(LCD_BL, OUTPUT);
  digitalWrite(LCD_BL, HIGH);

  ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL;
  ...

  #if (OMOTE_HARDWARE_REV == 1)
  // Slowly charge the VSW voltage to prevent a brownout
  // Workaround for hardware rev 1!
  Serial.println("Will slowly charge VSW voltage to prevent that screen is completely bright, with no content");
  for(int i = 0; i < 100; i++) {
    digitalWrite(LCD_EN, HIGH);  // LCD Logic off
    delayMicroseconds(1);
    digitalWrite(LCD_EN, LOW);   // LCD Logic on
  }
  #else
  Serial.println("Will immediately charge VSW voltage. If screen is completely bright, with no content, then this is the reason.");
  digitalWrite(LCD_EN, LOW);
  #endif

  // Setup touchscreen
  touch.begin(128); // Initialize touchscreen and set sensitivity threshold

}
CoretechR commented 3 months ago

Sorry @KlausMu, but I'm a little confused. I can not find init_hardware_general_HAL or init_hardware_general() anywhere in the repository. Are you not referring to the latest code on the main branch?

KlausMu commented 3 months ago

No, sorry, it is in the feature branch. https://github.com/CoretechR/OMOTE/blob/lvgl-simulator/Platformio/hardware/ESP32/hardware_general_hal_esp32.cpp

I think we should not try to find a possible fault in main, if in the feature branch a lot changed again.

CoretechR commented 3 months ago

Ah, I see. With your suggested changes it looks like this: grafik The flicker is still visible both on the screen and in the scope.

CoretechR commented 3 months ago

However it seems to start a lot faster with your changes. Here is the unchanged code from the feature branch (not 200us but 200ms per div this time): grafik And with your changes: grafik The issue only seems to be with the very first few us where the ESP32 initializes its pins.

Also, init_sleep_HAL should be called earlier as it disables the hold on the GPIOs. I used this to keep the backlight and display power signals high (disabled) during sleep and wakeup. The pins should not work before init_sleep enables them.

EDIT: Now what I did in addition to your changes:

With these changes combined, there seems to be no flicker at all anymore an the ESP32 seems to start smoothly and quickly: grafik (200 us per div)

CoretechR commented 3 months ago

@KlausMu I think the modular code definitely needs the pin initialization in the right order:

  1. Set pin direction and state to default (backlight and LCD off) value.
  2. disable GPIO hold
  3. turn on LCD/VSW

(I remember that the GPIO hold was tricky to set up. The Espressif code and documentation seemed to be a little unfinished for this feature)

Maybe the GPIO code is best placed in init_hardware_general?

KlausMu commented 3 months ago

Ok, I understand. Didn't know that the correct order is so crucial. Sorry. So it has to look like this:

called at the very beginning of main:
void init_hardware_general_HAL(void)
  pinMode(LCD_EN, OUTPUT);
  digitalWrite(LCD_EN, HIGH);
  // this is power for backlight LEDs
  pinMode(LCD_BL, OUTPUT);
  digitalWrite(LCD_BL, HIGH);
}

as second in main:
void init_sleep_HAL() {
  gpio_hold_dis((gpio_num_t)LCD_EN);
  gpio_hold_dis((gpio_num_t)LCD_BL);
  gpio_deep_sleep_hold_dis();
}

can be one of the latest in main:
void init_tft(void) {
  ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL;
  ...

  #if (OMOTE_HARDWARE_REV == 1)
  // Slowly charge the VSW voltage to prevent a brownout
  // Workaround for hardware rev 1!
  Serial.println("Will slowly charge VSW voltage to prevent that screen is completely bright, with no content");
  for(int i = 0; i < 100; i++) {
    digitalWrite(LCD_EN, HIGH);  // LCD Logic off
    delayMicroseconds(1);
    digitalWrite(LCD_EN, LOW);   // LCD Logic on
  }
  #else
  Serial.println("Will immediately charge VSW voltage. If screen is completely bright, with no content, then this is the reason.");
  digitalWrite(LCD_EN, LOW);
  #endif

  // has to be here, not earlier, because I2C is not available before
  // Configure i2c pins and set frequency to 400kHz
  Wire.begin(SDA_GPIO, SCL_GPIO, 400000);
  touch.begin(128);
}

has to be after init_tft(), otherwise I2C will not work
void init_IMU_HAL(void) {
  IMU.begin();  
}

It's not so nice from a modular perspective, that's why I changed it, but when the hardware needs it that way ...

This is now exactly the same order as in the old code. I flashed it and it looks good to me. If you want me to, I can commit it, so that you can try it with the scope.

CoretechR commented 3 months ago

That seems right. And yes, unfortunately it makes the code less modular in a way.

If you want, I can test your updated code. I have everything still set up on my desk :) It would also be interesting to check the current consumption in sleep mode, as that is also affected by the GPIO hold.

KlausMu commented 3 months ago

I pushed it to branch lvgl_simulator.

CoretechR commented 3 months ago

That looks perfect, no backlight flickering and the screen turns on quickly. Thank you, Klaus!

I wonder if this also affects the LCD initialization issue. @JustMe-NL Could you please check if the latest code in the lvgl_simulator branch improves the screen problem?

KlausMu commented 3 months ago

That looks perfect, no backlight flickering and the screen turns on quickly. Thank you, Klaus!

Thank you, @CoretechR , for testing! Maybe we can get rid of this line as well? https://github.com/CoretechR/OMOTE/blob/dbfa058d2289b2e729489a43915d3db9f8e0f12f/Platformio/hardware/ESP32/tft_hal_esp32.cpp#L64 Including WiFi, my OMOTE finishes setup in 900 ms, without this line in 800 ms Did a quick test, to me there was no difference. Maybe some long term test was better.

JustMe-NL commented 3 months ago

I haven't done a long term test but the results so far are, that with the latest firmware from lvgl_simulator, the white screen has not yet been seen.

And that test includes commenting out the delay(100). But if that would cause concerns we could always move the LCD_EN enablement to before the PWM channel initialization, so the screen has a little more time to stabilize.

CoretechR commented 3 months ago

Thanks, @JustMe-NL, that sound promising! Then we probably don't need a hardware change.

I originally included the 100ms delay because the datasheet mentions a minimum reset complete time of 120ms: grafik In practice, this is probably a lot faster so we could reduce the time. But I am a little reluctant to get rid of the delay altogether. Maybe reduce it to 10ms and see if that works in the long term?

KlausMu commented 3 months ago

I did some profiling. This is how it comes to the 900 ms:

1:  55 until first statement in main()
2:   2 ms for preferences, led, battery()
3:   1 ms for keys()
4:   0 ms for IR sender()
5: 274 ms for ble keyboard
6:   9 ms for register devices
7:   0 ms for register guis
8: 468 ms for init_gui()
   81:   1 ms for lv_init()
   82: 334 ms for init_lvgl_hardware
       821: 330 ms init_tft()
            8211:   0 ms for setting up lcd channel
            8212:  10 ms for LCD_EN_GPIO (would include 100 ms, if activated)
            8213: 272 ms for tft.init()
            8214:   1 ms for tft.initDMA()
            8215:   0 ms for tft.setRotation(0);
            8216:  32 ms for tft.fillScreen(TFT_BLACK);
            8217:   0 ms for tft.setSwapBytes(true);
            8218:   3 ms for Wire.begin(
            8219:  12 ms for touch.begin
       822:   4 ms display driver()
       823:   0 ms touch driver()
   83: 68 ms for lvgl base objects
   84: 65 ms for the first three screens (might be 0 if no screen is created)
9:  69 ms for first gui_loop()
A:   2 ms for init_IMU
B:   5 ms for register scenes
C:  36 ms for wifi mqtt
Setup finished in 923 ms.

So 8212 is the one we want to optimize, and the question is if delay(100) can be deleted. 8213 is already using the TFT, so the best would be to have something between 8212 and 8213 which needs some time, but is not using the TFT.

83 and 84 are the best candidates, because they need the most time.

So I did a test. I wanted to move 8212, 83 and 84 between 81 and 82. For this to work, 822, 823 also had to be moved.

With that, we have the needed delay of at least 72 ms. Normally even more, about 130 ms, because the first three tabs also have to be created.

So my suggestion is to do this movement in code. Yes, there is some risk in it, and @CoretechR should do the hardware measurements afterwards once again.

The other option is, to leave everything as it is and to reduce the delay(100) to delay(10).

What do you think?

Update: ESP32 was happy with this change, but not the simulator. Since the simulator is much faster, it often is doing things in a different order. LVGL is working a lot with threads or so called timers, which behave differently on the simulator and on ESP32. So I would not do the change.

@CoretechR So delay(10) instead of delay(100)? But if there is a risk to get into trouble again, I would not do it.

JustMe-NL commented 3 months ago

Despite the updated timings, I'm still experiencing sporadic white screens. But in the quest to quench them out I noticed that the quoted datasheet from @CoretechR mentioned two timing minimums for reset:

313409372-5ff31b81-2126-404e-83e7-61311c0479e5 SPLIN mode @ 5 ms and SPLOUT mode @ 120 ms. But in which mode is the LCD after startup and what does tft.init() actually do? The tft.init() function issues a software reset when no hardware pin is defined for reset (TFT_RST<0), plus the library adds it's own 150ms delay after reset. From the library TFT_eSPI.cpp in void TFT_eSPI::init(uint8_t tc) @ line 684:

#ifdef TFT_RST
  #if !defined(RP2040_PIO_INTERFACE)
    // Set to output once again in case MISO is used for TFT_RST
    if (TFT_RST >= 0) {
      pinMode(TFT_RST, OUTPUT);
    }
  #endif
  if (TFT_RST >= 0) {
    writecommand(0x00); // Put SPI bus in known state for TFT with CS tied low
    digitalWrite(TFT_RST, HIGH);
    delay(5);
    digitalWrite(TFT_RST, LOW);
    delay(20);
    digitalWrite(TFT_RST, HIGH);
  }
  else writecommand(TFT_SWRST); // Software reset
#else
  writecommand(TFT_SWRST); // Software reset
#endif

  delay(150); // Wait for reset to complete

  begin_tft_write();

And the detailed datasheet of the ILI9341 @ https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf mentions the PowerOn sequence at page 214:

Scherm­afbeelding 2024-03-23 om 23 38 41

And states the LCD is in Sleep IN (SLPIN) mode after Power On. So this could mean that a delay of 5 ms between LCD_EN_GPIO = LOW & tft.init() is more than enough.

In my quest to hunt down the white screen I also added an APX803S reset IC to the reset line as used in the Adafruit 1770 breakout board for the exact same LCD screen (actually, that breakout board donated the APX803 as well as the LCD). Setting TFT_RST to 0 in platformio.ini eliminates the software reset in tft.init() and GPIO0 is "useless" anyway. This works perfectly but alas does not completely eliminate my problem. Now that I know for sure the reset is not the issue, thanks to the APX803S, I need to look further and saw that sometimes the white screen appears even when the LCD was awake and functional. Looking at the detailed datasheet, it also mentions that the maximum frequency of the SPI is 10MHz (100ns pulsewidth of SCL - page 243). So that will be my next try, to lower the frequency, maybe my LCD is that one specimen that can't do overclocking :( .

KlausMu commented 3 months ago

Man, you know what you are doing.

In my case, if I have the white screen, the LCD is always awake and functional. I'm using 40 MHz SPI. I even tried 80 MHz, but with that high the LCD is unstable.and starts flickering. Setting SPI to 10 MHz turned my LCD unacceptably slow during the animations.

Regarding the delay: I will reduce it from 100ms to 5ms. @CoretechR ok for you?

JustMe-NL commented 3 months ago

Yes, I agree. 10MHz is too slow for the animations. However from 24MHz and up it begins to look Ok. At 32MHz you barely see the difference with 40Mhz.

Assuming (did not verify) we use 16bits per pixel you get the following framerates: 10MHz - 8 24MHZ - 19 32MHZ - 26 40MHZ - 32

So some tweaking is possible.

Would be nice to figure out why some screens can easily handel 80MHz+ and some cannot though.

KlausMu commented 3 months ago

Yes, 16bit for colors is correct. You can add -D LV_USE_PERF_MONITOR=1 to platformio.ini to see framerate. With that you can see that framerate is not constant.

It's interesting that lvgl itself is always reporting 33 Hz when no animation is running. No matter which SPI frequency. But if an animation is running, situation is a lot different and depends a little bit on how many objects are on the currently active gui.

Only during animation: 10MHz - 5 fps 24MHZ - 8 fps 32MHZ - 10 fps 40MHZ - 12 fps 80MHZ - 14 fps

The simulator always shows 33 Hz, even during animation.

CoretechR commented 3 months ago

Thank you, @JustMe-NL! I did not know that there already was a delay within the library. Then we can definitely reduce the delay in the software to 5ms. When using SPI, there is really no other way than to increase the speed way above 10MHz if you want smooth animations. I have one older Adafruit display that works fine at 80MHz. But maybe other displays will show issues like the white screen at only 40MHz. This could be solved by connecting the display to the ESP32 in parallel mode. But in order to free enough GPIOs for the interface, basically the entire pin mapping would have to be changed and a multiplexer added for the buttons grid. I have been considering this and it could be done, but I unsure if it is worth it.

JustMe-NL commented 3 months ago

So something does not compute with the reported frame rates of LVGL. If the frame rate @ 10MHz is 5, that would mean a theoretical maximum of 250.000 bytes per frame are being transmitted. But @ 32MHz it reports 10 frames what theoretically could be 16 (32000000/250000). So about 38ms per frame are unaccounted for. (42ms @ 24MHz & 27ms @ 40 MHz).

That being said, a frame rate of 12 is the minimum for animations and from 24 MHz and up it seems fluid to me?

KlausMu commented 3 months ago

I think the fps reported by lvgl is the result of different things. SPI frequency is only one part. CPU load and how long it takes to calculate the next frame is most likely another player.

And LVGL uses a fixed limit of

/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 30      /*[ms]*/

24 MHz: I definitely can see a jerky animation, and touch doesn't behave very well 32 MHz: much better 40 MHz: a little better, recognizable, but not that much of a big improvement

So from what I can recognize, there is a big improvement from 24 to 32, and a smaller improvement from 32 to 40.

@CoretechR I committed 5 ms

JustMe-NL commented 3 months ago

Ok, at 40 MHz I got white screen problems but after some carefull testing I can safely say that it works reliable at 38MHz on my screen & hardware. I have not figured out yet if we could change something in the hardware to obtain those desired higher speeds (some extra caps maybe?). Projects that boast about higher speeds usually do not post their schematic or hardware layout.

My added APX803S could be unnecessary but maybe consider adding it as an fail-safe option to hardware rev 4, just add the footprint and do not populate it unless you have starting issues with the screen.

CoretechR commented 3 months ago

Interesting, that there is such a big difference with different SPI speeds. I will definitely add an optional reset generator to the next revision. Thank you for the suggestion!

JustMe-NL commented 2 months ago

The saga continues when you follow the white screen into the rabbit hole, or something like that.

So my Omote SPI runs at 78MHz with an apx803 and TFT_RST set to GPIO0. This means the library does not issue a soft reset. And I have not seen a white screen since until I had to check my USB port (because a macOS update probably screwed with the ch3040 driver). That is great news! After opening the case and resoldering the USB, I had some issues but they disappeared after I reseated the LCD cable (and the SPI speed had no influence whatsoever).

And now for the fun part: I made a second Omote without any hardware alterations and loaded the exact same code and it too has zero issues, running at 78MHz but without the apx803 and without the soft reset because the TFT_RST is still configured to GPIO0.

Hindsight is always 20/20 right? Looking back I think all problems disappeared after I restarted my customizations from scratch with the scene_selection (love it by the way!) repo. Somewhere I previously must have overlooked something in the startup sequence somewhere in the code.

For those folks who have read until here and are having the same problems, check these things first:

KlausMu commented 2 months ago

Oh, that's good to hear, thank you for the update. Since the branch "scene-selection" seems to be good, I brought it back to main.

KlausMu commented 2 months ago

One more thing I found out about SPI. There exists the so called GPIO Matrix and IO_MUX

IO_MUX means, you are using the SPI pins that are intended for that use. In that case you can use up to 80 MHz. GPIO Matrix means, you are using different GPIO pins for SPI. In that case you can use only up to 40 MHz.

OMOTE is using IO_MUX (SPI 3 or VSPI). OMOTE is not using MISO (GPIO 19) for SPI. But GPIO 19 is used for SDA (I2C).

Don't know if this different use of GPIO19 is of any relevance or if it could disturb or limit SPI.

CoretechR commented 2 months ago

Thank you for the interesting update, @JustMe-NL! You mean 38 MHz, right? Glad to hear that your remote is running without issues now. I included a spot for the APX803S in PCB revision 4 anyway.

CoretechR commented 2 months ago

One more thing I found out about SPI. There exists the so called GPIO Matrix and IO_MUX

IO_MUX means, you are using the SPI pins that are intended for that use. In that case you can use up to 80 MHz. GPIO Matrix means, you are using different GPIO pins for SPI. In that case you can use only up to 40 MHz.

OMOTE is using IO_MUX (SPI 3 or VSPI). OMOTE is not using MISO (GPIO 19) for SPI. But GPIO 19 is used for SDA (I2C).

Don't know if this different use of GPIO19 is of any relevance or if it could disturb or limit SPI.

Yes, with the way the display is connected we can make use of the IO_MUX and drive the display at 80MHz. But I never heard of unused pins causing problems, especially at below 40MHz. Technically, chip select, QUADWP and QUADHD are also part of the SPI IO_MUX. But who knows, the ESP32 works in weird ways sometimes.

JustMe-NL commented 2 months ago

Thank you for the interesting update, @JustMe-NL! You mean 38 MHz, right? Glad to hear that your remote is running without issues now. I included a spot for the APX803S in PCB revision 4 anyway.

Nope, 78MHz as in "-D SPI_FREQUENCY=78000000". But to be frank, I did not check this with spi_device_get_actual_freq.

JustMe-NL commented 2 months ago

After adding Serial.printf("SPI clock set to: %d\r\n", spi_get_actual_clock(APB_CLK_FREQ, SPI_FREQUENCY, 128)); the serial console informs me the frequency is set to 80MHz, and working fine on multiple LCD screens.