Skip to content
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

Problem with text display on ST7789 when LoRa device on same SPI bus #3208

Open
StuartsProjects opened this issue Mar 6, 2024 · 10 comments
Open

Comments

@StuartsProjects
Copy link

I have written an application which makes use of the TFT_eSPI library and TFT_eFEX extension.

I am using and ESP32S3 Dev Kit board, Arduino IDE 1.8.13, TFT_eSPI 2.5.34, ESP32 plugin 2.0.11 and my own LoRa library; all on Windows 10.

The application involves JPG images being transferred via LoRa from a transmitter (could be an ESP32CAM) to a receiver and then displayed on a TFT. The receiver saves the incoming image to an array in PSRAM and I then use the TFT_eFEX extension to display the image on the TFT, this does all work.

However there are some issues writing informative text messages to the TFT display, in that the first character of a text string can be missing. For instance if you use tft.print(“Hello World”); then the screen shows ello World, the H is missing.

I eventually narrowed it down to some form of conflict with SPI.beginTransaction(). I have #define SUPPORT_TRANSACTIONS enabled in User_Setup.h and the LoRa library uses SPI.beginTransaction() and SPI.endTransaction() for each SPI access as would be normal.

Here is a minimalist program that shows the issue, all that is happening is that there is an SPI read of a LoRa device register. If the register read does not use SPI.beginTransaction() and SPI.endTransaction(); then the text on screen displays as;

Line 1
Line 2
ABCDEFGHIJKLMNO

Which is as expected. However, if that LoRa device register read does use SPI.beginTransaction() and SPI.endTransaction();, which is the normal usage of the library then the text on screen displays as;

ine 1
Line 2
ABCDEFGHIJKLMNO

The L on Line 1 is missing.

#include <SPI.h>

#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();

#define NSS 10
#define NRESET 1
#define DISPCS 40

#define USE_SPI_TRANSACTION

void loop()
{
}

void setup()
{
  digitalWrite(NSS, HIGH);                  //disable LoRa device
  pinMode(NSS, OUTPUT);
  digitalWrite(NRESET, HIGH);
  pinMode(NRESET, OUTPUT);
  digitalWrite(DISPCS, HIGH);               //disable display 
  pinMode(DISPCS, OUTPUT);

  tft.begin();
  tft.setRotation(1);                                  // 0 & 2 Portrait. 1 & 3 landscape
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);

  //***************** start read from SPI LoRa device
#ifdef USE_SPI_TRANSACTION
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#endif

  digitalWrite(NSS, LOW);
  SPI.transfer(0 & 0x7F);                         //mask address for read
  uint8_t regdata = SPI.transfer(0);        //read a byte
  digitalWrite(NSS, HIGH);

#ifdef USE_SPI_TRANSACTION
  SPI.endTransaction();
#endif
  //***************** end read from SPI LoRa device

  tft.setCursor(0, 20);
  tft.print("Line 1");
  tft.setCursor(0, 40);
  tft.print("Line 2");
  tft.setCursor(0, 60);
  tft.print("ABCDEFGHIJKLMNO");
}

Any ideas ?

@Bodmer
Copy link
Owner

Bodmer commented Mar 6, 2024

The two libraries will be using different instances of the SPI class on the same hardware port so they do not cooperate with hardware access.

The Lora and TFT_eSPI classes must use the same instance. You can init the TFT library then get the instance to be used in the Lora library. Use this library function:

static SPIClass& getSPIinstance(void); // Get SPI class handle

Note, if the Lora library uses interrupts to access the SPI port then that will still cause problems.

@StuartsProjects
Copy link
Author

Thanks for the reply.

Cant say I recall having similar problems with mixing SPI devices and libraries on the same SPI bus, LoRa devices, displays, SD cards etc.

I just tried the example I posted with an Adafruit_ILI9341 on the ESP32S3, that seems to work as is, no changes to code or SPI instances needed.

@StuartsProjects
Copy link
Author

Futher comment.

In the example code I posted, thereare reads to the SPI. interface, which is the default SPI interface for the ESP32S3 (same as VSPI ?)

Does not the TFT_eSPI library point to the same physical hardware interface, even if it is using a different name ?

The LoRa device and display are on the same pins SPI bus pins afterall.

@zoelechat
Copy link

Hello,

I had similar problem despite using separate SPIs (ESP32), and realized that Lora (actually sx1276) module was always "leaking" into the last drawn object. Now I always end any screen update by a fillRect 1x1 in a corner, and the leak remains enclosed there as a kind of "heartbeat" blinking pixel :D

@StuartsProjects
Copy link
Author

StuartsProjects commented Mar 7, 2024

Thanks for the comment @zoelechat, however ……....

If your using a LoRa device then you might assume the problem is something to do with the LoRa library or device, when the reality appears to be there is an issue when there is just an attempt of SPI access to another device on the same bus as the TFT even if there is no actual device there.

Take the example program below, its just a basic SPI read and there does not need to be an actual device (using the NSS pin) on the SPI bus at all;


#define NSS 10
#define DISPCS 40

#include <SPI.h>

#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();

SPIClass& SPIx = SPI;      //Create a class variable to hold the TFT_eSPI SPI class

#define USE_SPI_TRANSACTION


void loop()
{
}


void setup()
{
  uint8_t regdata;
  pinMode(NSS, OUTPUT);
  digitalWrite(NSS, HIGH);                  //disable device, which does not need to be there 
  pinMode(DISPCS, OUTPUT);
  digitalWrite(DISPCS, HIGH);               //disable display 

  tft.begin();
  tft.setRotation(1);                       //0 & 2 Portrait. 1 & 3 landscape
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);
  
  SPIx = tft.getSPIinstance();

//***************** start read from SPI bus
#ifdef USE_SPI_TRANSACTION
  SPIx.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#endif

  digitalWrite(NSS, LOW);
  SPIx.transfer(0x42 & 0x7F);                //mask address for read
  regdata = SPIx.transfer(0);                //read a byte
  digitalWrite(NSS, HIGH);

#ifdef USE_SPI_TRANSACTION
  SPIx.endTransaction();
#endif
//***************** end read from SPI 

  //it appears that after the SPI bus is used by another device the next TFT draw command is ignored
  //So when printing text first draw something that has no real visible effect on the display .........
  //tft.drawPixel(5, 5, TFT_WHITE);
  tft.setCursor(0, 20);
  tft.print("Line 1");
  tft.setCursor(0, 40);
  tft.print("Line 2");
  tft.setCursor(0, 60);
  tft.print("Register read 0x");
  tft.print(regdata,HEX);
}

The code reading the SPI bus would read register 0x42 of a LoRa device if it was connected and will return 0x12 if an SX1278 is there, but the device does not have to be there to cause problems with the display, so to test you can just leave it out.

Now with SPItransactions enabled it appears that after an SPI bus read the next draw command used by the TFT_eSPI library is ignored. So the first draw of the command tft.print("Line 1"); would be to draw the L character and in the code above the L character is missing from the display, only 'ine 1' is printed.

However if just before the tft.print("Line 1"); you add a tft.drawPixel(5, 5, TFT_WHITE); command, the draw of the pixel does not take place and as if by magic 'Line 1' is now printed correctly.

So not a LoRa problem at all but some issue with ESP32 and SPItransaction ?

@zoelechat
Copy link

Not sure it's exactly the same problem then... What I noticed looked more like "binary data on screen" starting from upper left corner of last drawn stuff, and going further at every sx1276 call as long as nothing is redrawn.
I don't use LoRa libraries at all but only some custom (and simple) stuff to read from weather station sensors.
Just to mention, the workaround to "enclose leak" doesn't work with drawPixel, that's why I use fillRect, even for a single pixel. Also I didn't try to draw it "out of screen" to simply hide it, because now I like that blinking pixel showing that "it's still alive"!

@Bodmer
Copy link
Owner

Bodmer commented Mar 7, 2024

The library by default uses SPI mode 3 for an ST7789 display so the clock state change may be the cause of the problem.
https://github.com/Bodmer/TFT_eSPI/blob/master/TFT_eSPI.h#L130-L137

Try your display with mode 0 by adding

#define TFT_SPI_MODE 0

to your setup file.

I would expect a display with a chip select to work OK but maybe the ESP32 board package does no change the clock polarity until the next SPI transmit operation? That might explain why the next command does no get recognised.

@zoelechat
Copy link

No chip select on my ST7789 (hence using both SPIs), and it's working only in mode 3 (cheap one I assume). And it took me a while to figure out why everything was constantly slow/hanging before I found to switch sx1276 to mode 3 as well.
I'm afraid I'm not able to understand what's happening in ESP32's head :)

@StuartsProjects
Copy link
Author

StuartsProjects commented Mar 7, 2024

Try your display with mode 0 by adding

#define TFT_SPI_MODE 0

In the simple example I posted above that change appears to solve the problem shown, Line 1 is printed in full.

Glad I was not going mad when I spotted the issue.

Is this one for espressif, who knows what the full consequnces of this 'change' in the ESP32S3 core might be ?

It will be a day or two before I can check on a basic ESP32.

Thanks for the response BTW.

@StuartsProjects
Copy link
Author

The same problem, missing writes to the display, does not occur on an ESP32 NodeMCU when User_Setup.h is set for the default SPI mode.

So where is the problem occuring, it appears that the SPI behaviour of the ESP32S3 and ESP32 are different, is this to do with the way the SPI bus is setup by the Espressif code or the TFT_eSPI library ?

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