Feather M4 + HX8357 TFT 8 bit parallel mode

Hello,
I recently acquired the 3.5 inch touchscreen (Adafruit 2050) and it works great.
However, I do find that SPI mode is very slow at redrawing the screen (I find over a second per refresh)
The reason I would need more speed is because I have text input with vertical scroll as well as whole-screen pictures. When I'm inputting and the screen is not full it works fine because I can just update that character.
But when the screen is full and I enter a newline the whole screen has to refresh because all the text has to get bumped up one line.
I tried turning on DMA ( by editing Adafruit_SPITFT.h and uncommenting the #define USE_SPI_DMA
line) but it's still slow.
So I thought the best way to increase the speed would be to use 8-bit parallel mode instead of SPI. I don't mind using all those extra pins because I only need one other one for this project.

I saw this video and the description says it uses ATSAMD51 which is what my Feather uses. Would it be possible to get the source code/library for that?

The Adafruit Learn article for my screen (here) has a tutorial for connecting via 8-bit parallel but it only shows the Arduino Uno wiring.
It also says:

The 8-bit mode is hand-tweaked in the Adafruit_TFTLCD pin_magic.h file. Its really only for advanced users who are totally cool with figuring out bitmasks for various ports & pins.

If that's the route I need to go (I'm assuming using the TFTLCD library and editing the pins_magic.h), how would I go about doing it? Or has someone done it already?

Either paste or attach your sketch. Preferably a minimal sketch that does not require exotic external hardware.

Whether you use SPI or Parallel vertical scrolling takes no time at all. Vertical scroll applies to portrait mode 320x480. In Landscape mode 480x320 it works left to right.

Most Arduino sketches work plenty fast enough if you apply some obvious tips e.g. only update areas that change.
Draw the whole screen once. Then just update small sections e.g. where icons, numbers or text change.

David.

Here’s a minimal example that prints 80 lines including the line number and the amount of millis to draw it

#include <Terminal.h>
#include <Adafruit_GFX.h>
#include <Adafruit_HX8357.h>

Terminal term;
Adafruit_HX8357 tft = Adafruit_HX8357(10,6,5);

void setup() {
  // put your setup code here, to run once:
  tft.begin();
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(0,0);
  tft.setTextColor(HX8357_WHITE, HX8357_BLACK);
  tft.setTextWrap(false);
  unsigned long startTime = millis();
  for (byte i = 0; i < 80; i++) {
    term.print("Line ");
    term.print(String(i));
    term.print("! ");
    //print time it took to draw line
    term.println(String(millis() - startTime));
    term.drawTerm(tft);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

Terminal.h is a class I wrote that writes to the TFT but also scrolls horizontally and vertically automatically. It works by writing to a buffer and then drawing the screen when drawTerm() is called.

Terminal.h

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_HX8357.h>

#ifndef terminal_h
#define terminal_h

#define TERM_WIDTH 53
#define TERM_HEIGHT 60

class Terminal : public Print {
  public:
    Terminal() {
      memset(buffer, 0, sizeof(buffer)); //clear buffer
    }
    
    size_t write(uint8_t a) {
      if (a == '\b') { //Backspace character
        if (buffer[cursorY].length() == 0 && cursorY > 0) {
          for (unsigned int i = cursorY; i < bufsize; i++) {
            buffer[i] = buffer[i+1];
          }
          cursorY--;
          bufsize--;
          cursorX = buffer[cursorY].length();
          if (cursorY < screenY + 3) {
            screenY--;
          }
        } else {
          buffer[cursorY] = buffer[cursorY].substring(0, cursorX-1) + buffer[cursorY].substring(cursorX, buffer[cursorY].length());
          cursorX--;
        }
      } else if (a == '\n') { //newline character
        cursorY++;
        bufsize++;
        cursorX = 0;
        for (unsigned int i = bufsize + 1; i > cursorY; i--) {
          buffer[i] = buffer[i-1];
        }
      } else {
        buffer[cursorY] = buffer[cursorY].substring(0, cursorX) + (char)a + buffer[cursorY].substring(cursorX, buffer[cursorY].length());
        cursorX++;
      }
      
      screenX = (cursorX > TERM_WIDTH) ? cursorX - TERM_WIDTH : 0;
      if (cursorY > screenY + (TERM_HEIGHT - 1)) {
        screenY = cursorY - (TERM_HEIGHT - 1);
      } else if (cursorY < screenY) {
        screenY = cursorY;
      }
      return 1;
    }

    size_t write(const uint8_t *buffer, size_t size) {
      size_t n = 0;
      while (size--) {
        if (write(*buffer++)) n++;
        else break;
      }
      //drawTerm();
      return n;
    }

    size_t write(const char *buffer, size_t size) {
      return write((const uint8_t *)buffer, size);
    }

    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }

    size_t print(const __FlashStringHelper *ifsh)
    {
      PGM_P p = reinterpret_cast<PGM_P>(ifsh);
      size_t n = 0;
      while (1) {
        unsigned char c = pgm_read_byte(p++);
        if (c == 0) break;
        if (write(c)) n++;
        else break;
      }
      return n;
    }

    size_t print(const String &s)
    {
      write(s.c_str(), s.length());
      return 0;
    }

    size_t print(const char str[])
    {
      write(str);
      return 0;
    }

    size_t print(char c)
    {
      write(c);
      return 0;
    }

    size_t println(void)
    {
      write("\n");
      return 0;
    }

    size_t println(const String &s)
    {
      print(s);
      println();
      return 0;
    }

    size_t println(const char c[])
    {
      print(c);
      println();
      return 0;
    }

    size_t println(char c)
    {
      print(c);
      println();
      return 0;
    }

    void drawTerm(Adafruit_HX8357 tft_out, boolean full=false) {
      tft_out.setCursor(0,0);
      for (unsigned int i = screenY; i < screenY + TERM_HEIGHT; i++) { //go from current screen y position
        //if the screen buffer line is not equal to the buffer
        // (or we want to update the full screen) update that line
        if (!screenBuffer[i-screenY].equals(buffer[i]) || full) {
          // if that line has text
          if (buffer[i].length() != 0) {
            screenBuffer[i-screenY] = buffer[i];
            tft_out.print(buffer[i].substring(screenX, (screenX + TERM_WIDTH < buffer[i].length() ? screenX + TERM_WIDTH : buffer[i].length())));
            for (int j = buffer[i].length(); j < TERM_WIDTH; j++) {
              tft_out.print(" ");
            }
            tft_out.println();
          } else {
            for (int j = 0; j < TERM_WIDTH; j++) {
              tft_out.print(" ");
            }
            tft_out.println();
          }
          screenBuffer[i-screenY] = buffer[i];
        // otherwise skip that line
        } else {
          tft_out.println();
        }
      }
    }

    void showTerm(Adafruit_HX8357 tft_out) {
      tft_out.fillScreen(HX8357_BLACK);
      drawTerm(tft_out, true);
    }

    void hideTerm(Adafruit_HX8357 tft_out) {
      tft_out.fillScreen(HX8357_BLACK);
    }

    void clearTerm(Adafruit_HX8357 tft_out) {
      memset(buffer, 0, sizeof(buffer)); //clear buffer
      cursorX = 0;
      cursorY = 0;
      screenX = 0;
      screenY = 0;
      tft_out.fillScreen(HX8357_BLACK);
      drawTerm(tft_out,true);
    }
  private:
    String buffer[2048];
    String screenBuffer[TERM_HEIGHT];
    unsigned int cursorX = 0;
    unsigned int cursorY = 0;
    unsigned int bufsize;
    unsigned int screenX = 0;
    unsigned int screenY = 0;
};

#endif

At the beginning when the screen is filling up it’s fine; takes about 30 ms per line:

Line 38! 1004
Line 39! 1030
Line 40! 1057
Line 41! 1083
Line 42! 1110
Line 43! 1136
Line 44! 1163

But after it gets to the bottom of the screen and starts vertical scrolling, it starts to take much longer:

Line 60! 3097
Line 61! 4635
Line 62! 6173
Line 63! 7711

Your program would not build on a Uno. So I built for an ILI9341 on a Due.

I am surprised that your Terminal class works at all. It is very convoluted. Strings make my head hurt. They will almost certainly crash at some point.

Please explain what you really want to do.
Implementing a Terminal that scrolls up is fairly simple. Especially if the "window" is as wide as the screen.
Implementing a Terminal that can scroll a single line left and right is ok.

Scrolling the whole screen left and right is expensive.
Keeping a copy of text that has scrolled up and out of the top of the screen is expensive.

In practice most applications might keep lots of data e.g. voltage, current, RPM, temperature, ...
But they seldom want to keep a large block of random text.

Something like a WYSIWYG screen editor is not suitable for a microcontroller. You have a PC and hard disk for that sort of thing.

Do you want a 53x60 character "window" ?
53 7x5 letters is 318 pixels wide and 60 lines is 480 pixels high.

Which looks like a whole 320x480 screen to me.

But storing 2000 lines seems unlikely. Especially if each line might be more than 53 letters.

David.

Sorry I should have mentioned that I am using an Adafruit Feather M4, which has 512KB flash and 192KB RAM (it does say that in the title, but I guess that's not obvious), but I guess a 2048 item buffer would probably be too much... Really the whole reason I need a buffer is so that you can scroll back up and see text history (see my project) and I doubt one would ever need to scroll back 2000 lines.

Ok here's my project (please note that this is just for fun):

I am currently developing a self-contained development environment for cortex-M based microcontrollers (like my Feather).
It is made using 2 boards: 1 for USB host and one as the actual computer. (I'm using a Trinket M0 for the USB coprocessor) This project allows you to program the board without the need for a PC. The programming language I'm using right now is Lua (LuaArduino library).

Planned features (I probably won't be able to do all of these):

  • USB Keyboard input (done, using this project)
  • Terminal-like interface w/ horizontal and vertical scrolling (that's the Terminal.h file)
  • Lua interpreter (integrated; added Arduino functions, graphics, etc.)
  • Ability to load and run programs from an SD card (working on)
  • Shell-like command line (working on)

Here's my project (note i made it in platformio)

Also I looked at the datasheet for HX8357 and I found a vertical scroll function but I'm not quite sure how I would implement that.
Also concerning Terminal.h I do realize using strings would probably be a better idea than Strings.

Basically I'm trying to find out how I can get better performance from my screen.
Sorry if I seem to be withholding information concerning my project; it's not on purpose, it's only because I thought I wouldn't need to go into the details