Addressing multiple SPI displays without blinking

Hi everyone,
I have been trying to get 8 OLED SPI displays working from an arduino while making use of an PCF8574 I/O-expander through I2C to save on GPIO pins.

After I while I found out:
1). I have to combine the real CS-signal (GPIO 0) from the arduino, through an OR-gate, with the signal from the I/O expander to make it work. Just pulling the CS down with the PCF8574 doesn’t give more than junk on the screen.

2). I have to use tft.begin() every time I have switched to another OLED, otherwise nothing comes on the screen. Also when I switch screens the first OLED goes out, and the second goes on.

Especially this last behavior is very annoying, because I would like the image to stay on the screens until I send another one. Now I have only alternating blinking OLED’s with the right image, but very short…

So my questions: Why do I have to send tft.begin() every time and not just in the beginning?
How can I keep the image on the screen until the update

Could anyone help me with that?

Here is some code to make clear how it’s built up (I cut a lot of WiFI and SD-card stuff out):

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>


//Pin definition for the OLED screen
#define sclk 14
#define mosi 13
#define cs   0
#define rst  15
#define dc   16

#define PCF8574_ADDR (0x38) //Address of the I2C port expander (all jumpers on 0)

// To draw images from the SD card, we share the hardware SPI interface
Adafruit_SSD1331 tft = Adafruit_SSD1331(cs, dc, rst);

// For Wemos D1 mini Pro we use the Wemos/Lolin SD-shield:
//Arduino     Shield
//14           CLK
//12           MISO
//13           MOSI
//2             CS

#define SD_CS 2    // Set the chip select line to whatever you use (not possible with shield of course...)

// Screen definitions
#define TFT1            0b11111110
#define TFT2            0b11111101
#define TFT3            0b11111011
#define TFT4            0b11110111
#define TFT5            0b11101111
#define TFT6            0b11011111
#define TFT7            0b10111111
#define TFT8            0b01111111


void setup() {

  Wire.begin(5,4);
  
  pinMode(cs, OUTPUT);    //CS for the OLED screens
  digitalWrite(cs, HIGH);

  //Initialise all displays
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT1);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT2);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT3);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT4);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT5);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT6);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT7);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT8);
  Wire.endTransmission();
  tft.begin();
  tft.fillScreen(BLUE);
  delay(500);

  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT1);
  Wire.endTransmission();
  //tft.begin();
  bmpDraw("logo.bmp", 0, 0);
  delay(1000);
 
  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT2);
  Wire.endTransmission();
  //tft.begin();
  bmpDraw("logo.bmp", 0, 0);
  delay(1000);

  Wire.beginTransmission(PCF8574_ADDR);
  Wire.write(TFT3);
  Wire.endTransmission();
  //tft.begin();
  bmpDraw("logo.bmp", 0, 0);
  delay(1000);

}


void loop() {
}

Do you really want eight 96x64 screens? Surely a single 240x320 would be more attractive. It would be cheaper and easier too.

I suspect you are using ESP8266 which has limited GPIO. The ESP32 gives you plenty of /CS signals without having to worry about PCF8574.

However, you should be able to select /CS individually via your PCF8574. You just operate the required mask value when the Adafruit_SSD1331 library wants to wiggle the CS line.

It should be simple enough to adapt the SSD1331 library. i.e. Wire.write(val ? 0xFF : CSPINMASK) instead of digitalWrite(CS, val)

David.

Edit. Changed 128x128 to 96x64.

Hi David, thank you for your reply. Eight screens are necessary because we glue them under eight horizontally next to each other positioned valves, to show the state of the valve realtime.

I have been using the code on ESP8266 and Arduino Nano to test why it went wrong, but it would be very nice to put everything on an ESP in the end. To be honest I didn't consider the ESP32 yet, will look into that!

I'm having a bit of a problem trying to understand the last part of your post. What do you mean by mask value? Like in Bitmask? Could you give one or two more hints, so I can look it up online?

This is of course just because i don't want to take the easy way out... :)

Mask just means a bit pattern like you have used for TFT1, TFT2, ... Often used with AND and OR and XOR when using Boolean logic.

In your case only one OLED should ever be selected at any one time. So your PCF8574 port will have value 0b11111111 when all displays are deselected. And TFTn when OLED#n is selected.

Seriously, buying a sensible 3.3V Arduino board is far easier than messing with PCF8574 e.g. Zero, Due, ESP32, STM32, ...

David.