Faster update on a ST7735 display

I've found an example of a much faster way to update a 160x128 ST77xx display here:
http://blog.dzl.dk/2020/04/04/crazy-fast-tft-action-with-adafruit-gfx-and-esp32/

Although written for an ESP32, looking at the code, it seemed it should work on a nRF52840.
However it isn't on my Nano 33 BLE + ST7735 assembly... The display lights up but it stays blank.
Maybe something to do with how the SPI transfer is implemented ?

Code follows:

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

//Display connections:
#define TFT_CS        10
#define TFT_RST        9
#define TFT_DC         8
//      TFT_SDA 23
//      TFT_SCL 18

Adafruit_ST7735 tft = Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST);   //-Just used for setup

class  aFrameBuffer : public Adafruit_GFX {
  public:
    uint16_t *buffer;
    aFrameBuffer(int16_t w, int16_t h): Adafruit_GFX(w, h)
    {
      buffer = (uint16_t*)malloc(2 * h * w);
      for (int i = 0; i < h * w; i++)
        buffer[i] = 0;

      tft.initR();
      tft.setRotation(1);
      tft.fillScreen(ST77XX_BLACK);
    }
    void drawPixel( int16_t x, int16_t y, uint16_t color)
    {
      if (x > 159)
        return;
      if (x < 0)
        return;
      if (y > 127)
        return;
      if (y < 0)
        return;
      buffer[x + y * _width] = color;
    }

    void display()
    {
      tft.setAddrWindow(0, 0, 160, 128);
      digitalWrite(TFT_DC, HIGH);
      digitalWrite(TFT_CS, LOW);
      SPI.beginTransaction(SPISettings(80000000, MSBFIRST, SPI_MODE0));
      for (uint16_t i = 0; i < 160 * 128; i++)
      {
        SPI.transfer16(buffer[i]);
      }
      SPI.endTransaction();
      digitalWrite(TFT_CS, HIGH);
    }
};

aFrameBuffer frame(160, 128);

int cnt = 0;
int cnt0 = 0;
int rate = 0;

unsigned long timer;

void setup() {
  timer = millis() + 1000;
}

void loop() {
  if (millis() >= timer)
  {
    timer += 1000;
    rate=cnt-cnt0;
    cnt0=cnt;
  }
  for (int y = 0; y < 128; y++)
    for (int x = 0; x < 160; x++)
      frame.drawPixel(x, y, random(65535));
  frame.fillRect(38, 58, 100, 15, 0);
  frame.setCursor(40, 60);
  frame.print("Frame:");
  frame.print(cnt++);
  frame.print("/");
  frame.print(rate);

  frame.display();
}

Surely you'd want your buffer to be private?

(Not your problem here, I know)

Do you have a schematic?

Here it is...
schematic.pdf (43.4 KB)

And code here (something was missing before)...

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

//Display connections:
#define TFT_CS        10
#define TFT_RST        9
#define TFT_DC         8
//      TFT_SDA 23
//      TFT_SCL 18

Adafruit_ST7735 tft = Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST);   //-Just used for setup

class  aFrameBuffer : public Adafruit_GFX {
  public:
    uint16_t *buffer;
    aFrameBuffer(int16_t w, int16_t h): Adafruit_GFX(w, h)
    {
      buffer = (uint16_t*)malloc(2 * h * w);
      for (int i = 0; i < h * w; i++)
        buffer[i] = 0;
    }
    void drawPixel( int16_t x, int16_t y, uint16_t color)
    {
      if (x > 159)
        return;
      if (x < 0)
        return;
      if (y > 127)
        return;
      if (y < 0)
        return;
      buffer[x + y * _width] = color;
    }

    void display()
    {
      tft.setAddrWindow(0, 0, 160, 128);
      digitalWrite(TFT_DC, HIGH);
      digitalWrite(TFT_CS, LOW);
      SPI.beginTransaction(SPISettings(80000000, MSBFIRST, SPI_MODE0));
      for (uint16_t i = 0; i < 160 * 128; i++)
      {
        SPI.transfer16(buffer[i]);
      }
      SPI.endTransaction();
      digitalWrite(TFT_CS, HIGH);
    }
};

aFrameBuffer frame(160, 128);

int cnt = 0;
int cnt0 = 0;
int rate = 0;

unsigned long timer;

void setup() {
  // display TFT pins
  pinMode(7, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);

  // display backlight pins
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);

  digitalWrite(3, true);
  digitalWrite(2, true);

  // turn on TFT pins      
  digitalWrite(7, true);
  digitalWrite(6, true);
  digitalWrite(5, true);
  digitalWrite(4, true);

  tft.initR(INITR_GREENTAB);      // Init ST7735S chip, green tab - for the Ebay Tfts
  tft.setRotation(1);
  tft.fillScreen(ST77XX_BLACK);
      
  timer = millis() + 1000;
}

void loop() {
  if (millis() >= timer)
  {
    timer += 1000;
    rate=cnt-cnt0;
    cnt0=cnt;
  }
  for (int y = 0; y < 128; y++)
    for (int x = 0; x < 160; x++)
      frame.drawPixel(x, y, random(65535));
      
  frame.fillRect(38, 58, 100, 15, 0);
  frame.setCursor(40, 60);
  frame.print("Frame:");
  frame.print(cnt++);
  frame.print("/");
  frame.print(rate);

  frame.display();
}

Now it's working but it isn't any faster than the usual way and nothing compared to what is shown in the video.... Maybe the difference is in the SPI implementation between nRF and ESP ?

1 Like

I was actually stuck on this problem and your answer helped me solve it. I did change this one line...

SPI.beginTransaction(SPISettings(80000000, MSBFIRST, SPI_MODE0));

to

SPI.beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE0));

and it woke right up. It's now running at roughly 23fps on an ESP32.