Problems with ST7735 160x80 0.96" SPI Display + Teensy 3.6

Status
Not open for further replies.

sid8580

Well-known member
Hi all - Haven't been able to get a generic ST7735 160x80 0.96" SPI Display working with a T3.6 (and now a T3.5 on a breadboard outside of my project).

This is what it looks like (identical physically but I bought on eBay, not this website):
https://www.buydisplay.com/default/...module-160x80-ips-st7735-with-arduino-library

I've tried the examples provided with the Adafruit ST7735 library, the Teensy-specific ST7335_T3 library, and the library provided in the link above (which doesn't compile, lots of library stuff seems to be missing or conflicting for that one). I can get the Teensy and Adafruit versions to compile, and I can see the example sketch running via serial monitor, but I am only getting a black screen (I can tell the backlight is functioning i.e. it's getting power but that's it). I've read through several threads in this forum about it and tried older library version, and completely removed/reinstalled Arduino & Teensyduino, swapped out libraries in the Teensyduino folders, etc... no luck here. I'm using the default pins on the Teensy that are specified in the code.

I'm not really clear on what the final fix ended up being, if someone could clarify I'd really appreciate it. I think I must have missed something, others have gotten this to work but I don't get why as I've tried everything I thought they did.

Thanks,
Owen
 
I've also made sure I select the correct initializer in the graphicstest.ino example each time... have also tried all the other options... basically every permutation I could come up with :)
 
Even though this thread pertains to the ST7789, I also tried as much as I could follow here since that display driver is shared with the ST7735:
https://forum.pjrc.com/threads/55317-New-adafruit-GFX-1-4-x-libs-doesn-t-work-for-me

I've spent probably a dozen hours on it at this point... going to just wait since I'm not getting anywhere... if there is anything else I should contribute I will; didn't post code so far since it's just the example sketches and many modifications thereof, but here is the last one (init stuff only, the rest is 100% unchanged out of the box):
Code:
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>

#ifdef ADAFRUIT_HALLOWING
  #define TFT_CS        39 // Hallowing display control pins: chip select
  #define TFT_RST       37 // Display reset
  #define TFT_DC        38 // Display data/command select
  #define TFT_BACKLIGHT  7 // Display backlight pin

#elif defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
  #define TFT_CS        44 // PyBadge/PyGamer display control pins: chip select
  #define TFT_RST       46 // Display reset
  #define TFT_DC        45 // Display data/command select
  #define TFT_BACKLIGHT 47 // Display backlight pin

#elif defined(ESP32)
  #define TFT_CS         5
  #define TFT_RST        22 
  #define TFT_DC         21
  //
  // define not needed for all pins; reference for ESP32 physical pins connections to VSPI:
  // SDA  GPIO23 aka VSPI MOSI
  // SCLK GPIO18 aka SCK aka VSPI SCK
  // D/C  GPIO21 aka A0 (also I2C SDA)
  // RST  GPIO22 aka RESET (also I2C SCL)
  // CS   GPIO5  aka chip select
  // LED  3.3V
  // VCC  5V
  // GND - GND
  //
#elif defined(ESP8266)
  #define TFT_CS         4
  #define TFT_RST        16                                            
  #define TFT_DC         5

#else
  // For the breakout board, you can use any 2 or 3 pins.
  // These pins will also work for the 1.8" TFT shield.
  #define TFT_CS        10
  #define TFT_RST        8 // Or set to -1 and connect to Arduino RESET pin
  #define TFT_DC         9
#endif

// OPTION 1 (recommended) is to use the HARDWARE SPI pins, which are unique
// to each board and not reassignable. For Arduino Uno: MOSI = pin 11 and
// SCLK = pin 13. This is the fastest mode of operation and is required if
// using the breakout board's microSD card.

#if defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
    // For PyBadge and PyGamer
    Adafruit_ST7735 tft = Adafruit_ST7735(&SPI1, TFT_CS, TFT_DC, TFT_RST);
#else
    // For 1.44" and 1.8" TFT with ST7735 (including HalloWing) use:
    //Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

    // For 1.3", 1.54", and 2.0" TFT with ST7789:
    //Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
#endif

// OPTION 2 lets you interface the display using ANY TWO or THREE PINS,
// tradeoff being that performance is not as fast as hardware SPI above.
#define TFT_CS        10
#define TFT_RST        8 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         9
#define TFT_MOSI 11  // Data out
#define TFT_SCLK 13  // Clock out

// For ST7735-based displays, we will use this call
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// OR for the ST7789-based displays, we will use this call
//Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);


float p = 3.1415926;

void setup(void) {
  Serial.begin(9600);
  Serial.print(F("Hello! ST77xx TFT Test"));

#ifdef ADAFRUIT_HALLOWING
  // HalloWing is a special case. It uses a ST7735R display just like the
  // breakout board, but the orientation and backlight control are different.
  tft.initR(INITR_HALLOWING);        // Initialize HalloWing-oriented screen
  pinMode(TFT_BACKLIGHT, OUTPUT);
  digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on

#elif defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
  tft.initR(INITR_BLACKTAB);        // Initialize ST7735R screen
  tft.setRotation(1);
  pinMode(TFT_BACKLIGHT, OUTPUT);
  digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on

#else
  // Use this initializer if using a 1.8" TFT screen:
  //tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab

  // OR use this initializer (uncomment) if using a 1.44" TFT:
  //tft.initR(INITR_144GREENTAB); // Init ST7735R chip, green tab

  // OR use this initializer (uncomment) if using a 0.96" 180x60 TFT:
  tft.initR(INITR_MINI160x80);  // Init ST7735S mini display

  // OR use this initializer (uncomment) if using a 1.3" or 1.54" 240x240 TFT:
  //tft.init(240, 240);           // Init ST7789 240x240

  // OR use this initializer (uncomment) if using a 2.0" 320x240 TFT:
  //tft.init(240, 320);           // Init ST7789 320x240
#endif

  Serial.println(F("Initialized"));

This part defines exactly how the pins are physically configured:
Code:
// OPTION 2 lets you interface the display using ANY TWO or THREE PINS,
// tradeoff being that performance is not as fast as hardware SPI above.
#define TFT_CS        10
#define TFT_RST        8 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         9
#define TFT_MOSI 11  // Data out
#define TFT_SCLK 13  // Clock out
 
It could be almost anything! For example it could imply be that display does not work! Sometimes when you buy these cheap displays on Ebay (or Ebay through Amazon), you run into issues. Example I purchased I believe three of the ST7789 displays without CS pins (probably the thread defragster pointed to). Two out of three had problems: (Actually 3 out of 4) - First one never worked, but it may have been my issue a year or two ago). But in this latest round: The first one from an Amazon shop never showed anything (Backlight dead), took some time, but they sent replacement which I have not tried yet. 2nd one from Ebay - Arrived with cracked display, but at least it showed some of the image. Third one from different Amazon reseller, worked...

That is why sometimes it pays to pay a bit more and purchase from someone like PJRC(ILI9341) or Adafruit... Who actually test the displays before they ship them, and hopefully the end customer does not have to deal with whatever percentage of them that are rejects.

But, back in July I did purchase one of the Adafruit 160x80 displays: https://www.adafruit.com/product/3533 And added the support to our version of the library (st7735_t3). I believe Paul pulled in these changes into the main fork/branch of the library on August 1st. So the current release of Teensyduino should have this support. I also added comment and commented out initR call for this display in the graphictest sketch:
Code:
// Or use this initializer (uncomment) if you're using a .96" TFT(160x80)
  //tft.initR(INITR_MINI160x80);

During my addition of the support for this display in our library, I also verified it worked with Adafruits version of the library.
 
Thanks guys for the responses, I'm attempting to get up to speed with the thread pointed out by defragster; that may take me a minute :)

I understand the value in buying components from a trusted vendor and will take that advice here... I usually go that route initially when I'm trying something new, sometimes after I've decided to use a part in greater numbers I may try alternative sources to save money. But I bypassed that phase this time since I'd had good luck with similarly sized OLEDs from the cheap vendors, figured it'd be fine... maybe not this time!

Does PJRC have tiny displays for sale? Hadn't seen any - if I found one that fit my needs I'd pick it up (especially since I'm going to order a T4.0 shortly! Awesome!).
 
I also forgot to mention, that as always it might help to see a picture of your setup.

You don't know how many times there have have been things like: Someone buys a new Teensy board and has some breakout pins, which they insert into a breadboard, and press the Teensy onto the pins and expect it to work, without actually soldering the breakout pins to the teensy. Or they forgot to run a GND wire, or they simply were off by one pin.

Or maybe there are forms of this display that require +5v to VCC or does it require only 3.3v, my quick look is that is requires 3.3v (min 3 max 4.8)

Also Did you supply voltage to the BLK pin (back light pin) and how? Run directly to 3.3v or did you try to connect to IO pin?
 
I verified my testbed T3.5 could drive the earlier OLED I was using, the jumper wires were good, tried every ST7735 I had on hand, tried a multitude of pin combinations (including the example graphicstest's predefined pins), tried playing with the BLK pin (disconnected, connected to 3.3v), tried running the whole ST7735 device from 5V (directly from USB V.in), tried swapping pins in case something was mislabelled during manufacturing on the TFT side... I'd post a pic but I don't have my phone at the moment and I don't know if it'd be worth doing after so many hours already. I think my ST7735's are likely just defective. Oh well, they were cheap.

So now I've ordered the 240x240 1.3" ST7789 from Adafruit (PJRC's ILI9341 looked great but slightly too large). This will be a more readable as the main display for the wearable MIDI controller I'm developing (ok... at this point, perfecting!), hoping this was all for the best as the tiny 0.96" might have worked for me but for some produced serious eyestrain.

Regardless... thanks for jumping in with helpful suggestions :)
 
I got this working with an Adafruit-supplied 1.3" TFT, ST7789, using the (very recently updated) ST7735_t3 library.

A couple of things -
1. Screen redraw seemed faster compared to the Adafruit ST77xx demo, but still slow enough to be kinda rough - is there any way to speed this up? Here is my init code -
Code:
// This Teensy3 and 4 native optimized and extended version
// requires specific pins. 
// If you use the short version of the constructor and the DC
// pin is hardware CS pin, then it will be slower.

// undefine this if you wish to try sync updates, note:
// some processors will undefine this.
#define USE_FRAME_BUFFER 

#define TFT_SCLK 32  // SCLK can also use pin 14
#define TFT_MOSI 0  // MOSI can also use pin 7
#define TFT_CS   30  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC   31  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST  29  // RST can use any pin
//#define SD_CS    55  // CS for SD card, can use any pin

// Note the above pins are for the SPI object.  For those Teensy boards which have
// more than one SPI object, such as T3.5, T3.6, T4 which have at SPI1 and SPI2
// LC with SPI1, look at the cards that come with the teensy or the web page
// https://www.pjrc.com/teensy/pinout.html to select the appropriate IO pins.

#include "Adafruit_GFX.h"  // Core graphics library
#include <ST7735_t3.h> // Hardware-specific library
#include <ST7789_t3.h> // Hardware-specific library
#include <SPI.h>

// Option 1: use any pins but a little slower
// Note: code will detect if specified pins are the hardware SPI pins
//       and will use hardware SPI if appropriate
// For 1.44" and 1.8" TFT with ST7735 use
//ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// For 1.54" or other TFT with ST7789, This has worked with some ST7789
// displays without CS pins, for those you can pass in -1 or 0xff for CS
// More notes by the tft.init call
ST7789_t3 tft = ST7789_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

I saw that note about the DC pin being the hardware CS pin, but if I think back to when I wired it I believe it wouldn't work with DC wired to the hardware CS pin... BUT I'm also not using the short version of the constructor... not sure if that matters? It does say it will autodetect. Not sure how to tell what it's actually doing.

I don't know what to really expect in terms of speed. I've just replaced an I2C 128x64 mono OLED and it seems the new TFT is faster, if I work within the original pixel dimensions of the OLED (pixel for pixel it's faster). But when I try using the entire display it's constantly refreshing, looks like an old TV where you can see the scan lines (even refreshing every 15ms).

I understand I should minimize what must be redrawn vs. wiping the entire screen with a black rectangle etc. every time, and plan to continue optimizing...

2. Anyone know how to get rid of the garbage/snow pixels while booting up? Never seen that before with the OLED, but the TFT looks like a static-filled television for about 500ms every time I power up!

Thanks and great work on the library support, critical stuff :D
 
AFAIK - The TFT's seem to show what is 'in memory' on power up - sometimes prior screen other times garbage.
 
Are we still on T3.6 with the 160x80 display? Like: https://www.adafruit.com/product/3533

Speed and/or what I might call perceived speed,

If you do things like:
tft.fillScreen(ST77XX_BLACK);
<draw new screen>

It will probably always look flashing... As sometimes you will see the black and then next refresh will see image...

There are many ways to fix this, Which includes:

a) only fill those portions of the screen that are not changing. i.e. only update each pixel once... In some cases this is easy to do.

b) cheat: The library has the concept in it of using a frame buffer (memory used to keep contents of logical display in it). So if you turn on using the frame buffer: tft.useFrameBuffer(1);
It will allocate enough memory, when it is turned on, all of the graphic primitives will write to memory instead of using SPI to talk to screen. When you are done with your changes, you can then do,
tft.updateScreen(); which will take the memory version and write it out to the screen, so each pixel is only output once.

There is secondary part to this as well, where you can use DMA to update the screen. The tft.updateScreen(); will update the whole screen before it returns. However tft.updateScreenAsync() will start up a DMA operation to update the display. While that is active you probably don't want to update the frame buffer...

There is also a continuous update version of the updateScreenAsync(true); where it starts up the DMA and continues to refresh. So in that case you do continue to do updates to screen, but the you are back to needing to carefully figure out how do your graphic primitives as to not cause flickering.

The Snow, I have seen it as well, not sure how best to know how to get rid of it. With some display setups I have seen where people tie the Reset pin of the display to the reset pin of the Teensy, that way when the system boots up, or reboots, the display will see the reset pin low and then go back high.. Again not sure if that would work for this on these displays...

Edit: Forgot to mention, yes you do get the best speed ups on T3.6 if the DC pin is on a hardware CS pin. Which I believe you have done with SPI1 and the hardware CS pins are:
(6, 31, 62), (58), (63)
Where you can only use one of each group in parentheses as a hardware CS... And the higher one are off of the SDCard...
 
Exactly what I was looking for KurtE... will give these methods a shot. And, the pins are configured as shown in my code so looks like I'm optimal there, glad to have a better understanding.

Yes, I'm on the 3.6 but with a new 1.3" 240x240 ST7789. Will be upgrading to the 4.0 soon due to the need for some frequently called interrupts to play nice with a small but growing GUI. Not sure if the 4's extra speed will have a big impact on how fast the screen can redraw (I'm not playing Doom, just some menus and bitmapped icons in color!) or if that is mainly a function of the SPI interface/ST77xx device...

The snow I'll deal with eventually, thanks for that suggestion too. I imagine there must be a way to avoid it or people would just avoid this display :)
 
Generally speaking, how much of an impact does the MCU speed have on graphics draw time?

I would assume a lot of impact when going from an AVR platform to ARM, but is there a point beyond which the SPI bus won't allow us to drive the screen any faster? Wondering if the T4.0 would make a big difference here or I'll be surprised to find that it really doesn't (though I need it for other operations that are stacking up, I guess that would be significant if those operations are slowing down ability to process my gfx commands).

I understand this question is very general and would depend on a lot of factors, wondering if there are any key points though to bear in mind...
 
This is a difficult thing to give a good answer to.

That is everything in this case is far more dictated by the speed of SPI.

Note: I am no expert when it comes to these devices. So not sure of how really fast one can reliably drive these devices and what other things that might influence these devices.

But suppose the maximum SPI speed that this device can handle is something like 24mhz (value came out of header file) Which my guess is this is over driving and may or may not work on all devices.

So the first thing that may influence how fast your program will update the screen, is how close to the 24mhz can you configure the SPI to work at.

That is if you do an SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
The system will try to find the fastest SPI speed it can do that does not exceed 24000000.
So for example if you are running T36 at 180mhz, the SPI speed will probably be:
BUS speed 90000000 (/2=45) (/3=30)(/4=22.5) So it will probably choose 22.5mhz SPI speed.

But if instead you choose: 192mhz then BUS=96, (96/4=24) so you get a speed up on SPI...


Next thing is for lack of better way of saying it, is how well can you keep the SPI buss busy? There are several things that influence it. However safe to say if your device code is
using simple SPI, transfer calls, it won't be optimal, but even with assuming you have a reasonably well done SPI library there are things that can help.

First off why is why is SPI.transfer(x) so relatively slow? It is because it not only transfers the data out that you put in it, it waits for the whole transfer to complete such that it can also return the value that may have been returned on the MISO pin. So it has to wait and queue is neutered and then it depends on how fast you can call it again.

But again there are things one can do, even at this level. Example if you are outputting a 16 bit value.
Some old code will do something like: SPI.transfer(val >> 8); SPI.transfer(val & 0xff);
So two calls through with two waits. But with Teensy 3.x/4.x and many others, you can do this with SPI.transfer(16, val);
..
Also Teensy and many others have transfer calls with buffers, which again allows the system to hopefully keep the queue full during the transfer.

But assuming the driver you are using understands the Teensy in this case 3.x. There are other things that speed things up:
Like for example when we do a fillRect(BLACK). For the moment ignore the beginning part of the transfer, but after that starts we will then output:
Something like 128x128x2 bytes to the display or how many rows*columns.

So we will end up putting on the SPI queue, the 128*128 words, so the SPI buss should be empty, and will output as quickly as possible.

Also since we are outputting words, there is usually a shorter time gap between the bytes of a word than there are between words on SPI. But this part should be output at the full speed of the SPI.

Now for the header part: Many times these devices have some form of header to say what rectangle we wish to write colors out to:
Something like: <CMD:ROWS>:<DATA: row start><DATA: row end><CMD: COLS><DATA start col><DATA: end col><CMD write memor> <Data .... all of our bytes

Now with this CMD: versus DATA, this is where the DC pin comes in. When DC is low then data is a command, when High it is data... So we must make sure that we synchronize when the data goes out to what the value of this pin is.

In most processors we do that by waiting until the SPI queue is full empty and the last data bit has been sent and then we change the state of DC and start up transfers again... Which again introduces wasted time on SPI bus.

But with libraries like ST7789_t3, when you have the DC pin on a hardware SPI CS pin, the T3.x chips allow us to encode the state of the hardware CS pins for the next transfer, such that we can encode this and have the SPI buss handle this for us without any unnecessary delay of SPI buss...

So again I know this is not a complete answer, but may give you some ideas on what things you need to look at.

Kurt
 
Status
Not open for further replies.
Back
Top