Multiple TFT Displays with Due using multiplexed SPI CS

Found something that might be of interest to others.

Its about using up to 30 TFT displays with an Arduino Due. The purpose is a Midi Foot Controller, where each button shall have a display indicating its actual function. There will be 4 layers with 26 buttons each and when switching between layers the displays shall indicate a text for the actual function for each button. So at a minimum I need to run 26 displays at the same time.

This is not possible using a TFT library multiple times where each display has its own CS line.

So I thought of using multiplexer chips for distributing the CS signal to each display. This way I only need to use one instance of the library with one CS output but I can address basically as many displays as I want.

My test setup is with 3 160x120 1.77" displays and the multiplexer chip is a 74HC4051. TI Datasheet

These CMos chips behave like selector switches and pass a signal from a common input to a selectable output. Its a 1to8 switch/multiplexer so it has 3 binary address lines for the switch selection. For my test setup I only needed two which I connected to my Arduino Due port 22 and 23. The input to the multiplexer is output 10 which is defined as CS in the code.

Here is a hardware diagram:

Now when switching outputs 22/23 to diffferent levels I am setting the multiplexer to direct CS to one of the 3 displays. This way I can easily control which display to send a text or other command to.

Here is the code:


#include <Adafruit_GFX.h>    // Core graphics library
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>

// Color definitions BLACKTAB are 565 RGB, GREENTAB = BGR (!!)

#define BLACK    0x0000
#define RED      0xF800
#define DRED     0x3000
#define BLUE     0x001F
#define DBLUE    0x0003
#define GREEN    0x07E0
#define DGREEN   0x0180
#define CYAN     0x0198
#define MAGENTA  0xF81F
#define YELLOW   0xFE40 
#define WHITE    0xFFFF
#define ORANGE   0xFA80

Adafruit_ST7735 tft = Adafruit_ST7735(10, 8, -1);

char CHR = 0;

void setup(void) {

  pinMode(22, OUTPUT);  // these two outputs control the multiplexer chip
  pinMode(23, OUTPUT); // which selects the actual display

  for(int n = 1; n < 4; n++){ //scroll through the 3 displays
      SetDisplay(n);  // selects the display

      tft.initR(INITR_BLACKTAB);
      tft.setSPISpeed(24000000); // Trying to set the SPI to higher clock. No change betwen 8.000.000 and 24.000.000 but 
                                                       // 1.000.000 is much slower
      tft.setFont(&FreeSansBold12pt7b);
      tft.setTextColor(WHITE);
      tft.setRotation(3); //I use them horizontally

      tft.fillScreen(BLACK); 
      tft.setCursor(5, 18);  
      tft.print("DISPLAY "); // Printing a label in each display
      tft.print(n);  
  }
} // end setup

void loop() {
    
    for (int M = 1; M < 4; M++){   //Scroll through the three displays      
        SetDisplay(M);
        tft.fillRect(0, 25, 159, 128, BLACK); //fill the variable text area
        tft.setCursor(5, 50);  
                
        for(int N = 0; N < 32; N++){ 
          //CHR = N;
          CHR = N + 32 + ((M-1)*32); //print CHR 32-64 on first display, 65-97 on second display, 98-.... on third display, just a test
          tft.print(CHR);
        }
    }
} // end loop

// This function selects the display by setting two binary outputs to the multiplexer chip
void SetDisplay(int DNo){
  if(DNo == 1){
    digitalWrite(22, LOW);
    digitalWrite(23, LOW); //binary 0
    tft.setTextColor(RED);
  }
  else if(DNo == 2){
    digitalWrite(22, HIGH);
    digitalWrite(23, LOW); //binary 1    
    tft.setTextColor(GREEN);
  }
  else if(DNo == 3){
    digitalWrite(22, LOW);
    digitalWrite(23, HIGH); //binary 2 
    tft.setTextColor(BLUE);
  }
}

This works really nice, I can address each display just by

SetDisplay(n)

and then write commands to fill the display with content. I am pretty sure that this will work for any number of displays, its just a matter of time/speed. However, the performance only for writing text to the displays is great. I dont need any graphics in my application. The only annoying thing is that every display needs roughly 500ms for initialisation with the Adafruit library. This means, my machine with 26 displays will require 15-20 seconds at least for booting up.

Please note:
The displays I am using are these ones:

  • They have to be initialized as BLACKTAB, else they show scrambled areas top and left. The documentation says GREENTAB, it seems to be wrong.
  • The reset line is tied to the due reset line for all displays.
  • I am using them with 'rotate = 1' (horizontally). This is perfect for a foot controller with regard to viewing angle.

Sorry, i couldn't find out how to post a video of the function here. If someone has questions please feel free to ask.

The use of an "open drain" multiplexer architecture is probably not optimal.

Here is a photo


of the test setup.

Can't see any problem with it so far. I have been quite deep into proAudio hardware and this kind of circuit is very common in old-school digital architectures. The pull-up resistors at the CS inputs of the displays are of course essential because when a display is inactive the multiplexer port will be high impedance.
However, I am not sure what max. speed could be handled by that circuit. I wasn't able to measure the actual SPI clock yet but it should be more or equal 8 MHz.

[Edit:] The TI datasheet of the 74HC4051 states a frequency response of the multiplexer switches of > 40MHz. Should be good for any SPI CS application with DUE.

BTW: More than 8 displays will simlpy require to expand the multiplexer size to 16, 24, 32 ..... This is fairly easy. I am just concerned about speed.

Hi,
Can you please post your code in code tags please?
To add code please click this link;

Thanks.. Tom... :grinning: :+1: :coffee: :australia:

Many thx for the hint Tom! I havn't been too busy in this forum for a while but this project will have its challenges .....

It is not related to the multiplexer capability, but rather to the use of the 10k pullup.

What issue are you thinking of?

Now it is getting interesting ...... I solved my boot time issue for many displays and found a nice trick to deal with several displays at once.

Here is a modified schematic:

The pull-up resistors to the displays are now not connected to 3V3 but to a DUE output (24). It means that when this output is LOW and all outputs from the multiplexer are OFF/ high-impedance then all display CS inputs are low/active. By selecting Out 4 of the multiplexer Out 1-3 go into high impedance.
I can then initialize all displays at once, fill the screens at once or write some text into all of them at the same time.

When setting output 24 back to HIGH the displays are again addressed individually by the multiplexer outputs.
Here is the code:

#include <Adafruit_GFX.h>    // Core graphics library
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>

// Color definitions BLACKTAB are RGB, GREENTAB = BGR (!!)

#define BLACK    0x0000
#define RED      0xF800
#define DRED     0x3000
#define BLUE     0x001F
#define DBLUE    0x0003
#define GREEN    0x07E0
#define DGREEN   0x0180
#define CYAN     0x0198
#define MAGENTA  0xF81F
#define YELLOW   0xFE40 
#define WHITE    0xFFFF
#define ORANGE   0xFA80

Adafruit_ST7735 tft = Adafruit_ST7735(10, 8, -1);

char CHR = 0;

void setup(void) {

  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT); //feeding the pull-up resistors

  SetDisplay(4); // set display 1-3 to inactive as long as feed into pull-up resistors is on HIGH
  digitalWrite(24, LOW); //the pull-up resistors are now on LOW, all display CS inputs are now LOW / active
  tft.initR(INITR_BLACKTAB); //all displays are initialized at once, not in sequence
  tft.setSPISpeed(24000000);     
  tft.setFont(&FreeSansBold12pt7b);
  tft.setRotation(3); //I use them horizontally
  tft.fillScreen(BLACK); 
  // now we return to individuall addressing of the displays
  digitalWrite(24, HIGH); //the pull-up resistors are on HIGH, from now on only the selected display is active  
  
  for(int n = 1; n < 4; n++){ //scroll through the 3 displays
  
  SetDisplay(n);  // selects the display
      tft.setTextColor(WHITE);
      tft.setCursor(5, 18);  
      tft.print("DISPLAY ");
      tft.print(n);  
  }
} // end setup

void loop() {
    
    for (int M = 1; M < 4; M++){ //Scroll through the three displays      
        SetDisplay(M);
        tft.fillRect(0, 25, 159, 128, BLACK); //fill the variable text area
        tft.setCursor(5, 50);  
                
        for(int N = 0; N < 32; N++){ 
          //CHR = N;
          CHR = N + 32 + ((M-1)*32); //print CHR 32-64 on first display, CHR 65-96 on second display (and so on) ... just a test
          tft.print(CHR);
        }
    }
} // end loop

// This function selects the display by setting two binary outputs to the multiplexer chip
void SetDisplay(int DNo){
  if(DNo == 1){
    digitalWrite(22, LOW);
    digitalWrite(23, LOW); //binary 0
    tft.setTextColor(RED);
  }
  else if(DNo == 2){
    digitalWrite(22, HIGH);
    digitalWrite(23, LOW); //binary 1    
    tft.setTextColor(GREEN);
  }
  else if(DNo == 3){
    digitalWrite(22, LOW);
    digitalWrite(23, HIGH); //binary 2 
    tft.setTextColor(BLUE);
  }
    else if(DNo == 4){  // there is no display #4 in this setup. It sets outputs 1-3 to high-impedance
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH); //binary 3 , CS outputs to display 1-3 are now 'high-impedance'.  
  }

}

The circuit now initializes all displays, rotates them to horizontal and fills them with black color at once in < 500ms. Then by setting output 24 back to HIGH the code returns to individual operation of each display.
I can at any time go back to controlling all displays at once:

SetDisplay(4);  //make multiplexer outputs 1-3 high impedance 
digitalWrite(24, LOW); //set the CS inputs of all displays to LOW

// do something on all displays at once
// fill screens, write text ......
// then go back to individual operation

SetDisplay(1);  //make display 1 active 
digitalWrite(24, HIGH); //feed the pull-up resistors with HIGH so that only the active display gets a LOW on CS
//do something on display 1

This is great for clearing all screens when new content shall be written into all displays because filling screens takes time. It now takes roughly 200ms for all displays at once.

This makes the solution practical, even for many displays.
Still need to improve the hardware, the feed into the pull-ups should be properly buffered.
However, this already works brilliant.

I have uploaded videos of the solution to YouTube.

Here is a video of the first version where displays are addressed sequentially: ARDUINO DUE running 3 TFT screens - first solution - YouTube

This one shows the enhanced version where displays can be addressed at once for initialization, fill screen and other functions: http://youtu.be/pxlm-ExBI6E

... never done a YouTube upload before, hope this works :slight_smile:

Next Version with 8 + 1 TFT displays.

I have added more displays to the multiplexer and another larger one I will need for a main menu.

Here is a video showing the function: 8 plus 1 TFT screen with Arduino DUE using SPI and multiplexed CS - YouTube

And here is the new schematic:

The code changes between drawing something on all displyas at once and individual addressing of singular displays. This is easy with this multiplexer arrangement. It saves time for booting up the system. You can see in the video how fast it initialises all displays at once.

Learnings:

  • The displays just need a hard coded CS line in order to work. No need to use SPI CS outputs for that
  • Pretty tricky to get the SPI bus running with that amount of cabling. I had to insert buffers. However, the DUE SPI port will not drive more than 3 74HC244 inputs. Then the clock becomes very slow and rounded. Not sure why that is the case.
  • This setup requires an extra 3.3V PSU, it draws 540mA for the 9 TFT's and the logic. The power rail needs to be stable.
  • The Adafruit libraries take dec numbers as color codes, no need to fiddle with HEX

Next Steps

  • add an SD Card slot and a RAM module to the SPI bus. I had both already running together but not with those displays
  • replace the multiplexer by a shift register. then I can address arbitrary combinations of displays as groups at once.
  • build the final structure with 24 TFT screens 160x128 and 1 TFTscreen 320x240 + SD Card + RAM on SPI.

Here is the code:

#include <Adafruit_GFX.h>    // Core graphics library
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>

// some custom fonts
#include <arial6pt7b.h>
#include <arial7pt7b.h>
#include <arial8pt7b.h>
#include <ariblk6pt7b.h>

#include <Adafruit_ST7735.h> // Library for the 8 160x128 diplays
#include <Adafruit_ILI9341.h> // Library for the larger 2.8" 320x240 display
#include <SPI.h>


Adafruit_ST7735 tft = Adafruit_ST7735(-1, 8, -1); // Initialized w.o. CS, CS is handled by the multiplexer
Adafruit_ILI9341 tft2 = Adafruit_ILI9341(27, 8); // Using a custom CS line keeping hardware CS lines free for SD Card and RAM

// global variables used in the code
char CHR = 0;
int BGCol = 0; //Color Variable
int TCol= 0; //TextColor Variable
int NrDisplays = 8; //Number of displays connected
int NrLogDisplays = 24; //Number of logical display addresses
int ColorIndex = 0; //an index used for selecting colors
String BtnMainMenu[7] = {"Cmd", "Type", "Delay", "Ch", "CC", "On", "Off"}; // Menu items in a screen
  
//32 Background Colors for the small displays
long TFTCol[32] = {20480, 34816, 57344, 57568, 63488, 55968, 55584, 64864, 64480, 41600, 21024, 960, 1606, 4642, 486, 650, 198, 12, 31, 1023, 1756, 49182, 63839, 40991, 57356, 0, 8516, 12808, 17034, 25582, 46712, 65535};
//32 TextColors for each Background Color
long TFTTCol[32] = {46712, 65535, 65535, 65535, 0, 65535, 65535, 0, 0, 65535, 65535, 65535, 0, 65535, 65535, 65535, 46712, 65535, 65535, 0, 0, 65535, 0, 65535, 65535, 46712, 65535, 65535, 65535, 0, 0, 0};
int ActBtnMenu = 0; //current MenuCursor
int LastBtnMenu = 0; //last MenuCursor
  
void setup(void) {

  pinMode(22, OUTPUT); //Out 22-24 control the mutiplexer address inputs
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT); 
  pinMode(25, OUTPUT); //feeding the pull-up resistors
  pinMode(26, OUTPUT); //4051 enable, low = active

// some push-button inputs:
  pinMode(50, INPUT_PULLUP);
  pinMode(51, INPUT_PULLUP);
  pinMode(52, INPUT_PULLUP);
  pinMode(53, INPUT_PULLUP);

  SetBtnDisplay(-2); // All BtnDisplays 
  randomSeed(analogRead(A0));
  tft.setSPISpeed(2000000); //Initialization works more reliable at lower SPI speed
      tft.initR(INITR_BLACKTAB); //all displays are initialized at once, not in sequence
      tft.setFont(&FreeSansBold12pt7b);
      tft.setRotation(3); //I use them horizontally 
  tft.setSPISpeed(16000000);  
  tft.fillScreen(0); 
  
  SetBtnDisplay(-1); // deactivate btn displays
  delay(20);  
  
  tft2.begin(8000000); //start Display #2 with 8MHz SPI Clock
  
  tft2.setRotation(3); //I use them horizontally
  tft2.fillScreen(0);
  MainRunDisplay(12);
  MainBtnMenu(0,0); //0 calls menu without cursor
  delay(200);  

  Serial.begin(38400);

  // BtnRunDisplay(0);
  
} // end setup

void loop() {

  BtnRunDisplay(0);
      for (int m = 0; m < NrLogDisplays ; m++){
      
          BtnNameDisplay(m);

          // This section takes input from two pushbuttons for the menu cursor
          if (digitalRead(50) == LOW){
            Serial.println("- gedrückt");
            if (ActBtnMenu > 0) {
              ActBtnMenu--;
              MainBtnMenu(LastBtnMenu,0);
              MainBtnMenu(ActBtnMenu,1);
              LastBtnMenu = ActBtnMenu;
            }
          }
        
          if (digitalRead(51) == LOW){
            Serial.println("+ gedrückt");
            if (ActBtnMenu < 12) {
              ActBtnMenu++;
              MainBtnMenu(LastBtnMenu,0);
              MainBtnMenu(ActBtnMenu,1);
              LastBtnMenu = ActBtnMenu;
            }
          }
      
      }
      ColorIndex = ColorIndex + 8;  //just making colors change for demo purposes
      if (ColorIndex + NrLogDisplays > 32) {ColorIndex = 0;}

} // end loop



void SetBtnDisplay(int DNo){  // This function selects the display by setting three binary outputs to the multiplexer chip
                              // It also controls the feed to the pull-up resistors and the enable input of the multiplexer
   digitalWrite(26, LOW); //set enable input of multiplexer to active
   digitalWrite(25, HIGH); //all pull-up resistors are on HIGH, only selected displays are active (active = LOW)
   
  if(DNo == 0){
    digitalWrite(22, LOW);
    digitalWrite(23, LOW); 
    digitalWrite(24, LOW); //binary 0
  }
  
  else if(DNo == 1){
    digitalWrite(22, HIGH);
    digitalWrite(23, LOW);   
    digitalWrite(24, LOW); //binary 1
  }
  
  else if(DNo == 2){
    digitalWrite(22, LOW);
    digitalWrite(23, HIGH); 
    digitalWrite(24, LOW); //binary 2
  }
  
  else if(DNo == 3){
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH); 
    digitalWrite(24, LOW); //binary 3
  }
  else if(DNo == 4){
    digitalWrite(22, LOW);
    digitalWrite(23, LOW); 
    digitalWrite(24, HIGH); //binary 4
  }
  
  else if(DNo == 5){
    digitalWrite(22, HIGH);
    digitalWrite(23, LOW);   
    digitalWrite(24, HIGH); //binary 5
  }
  
  else if(DNo == 6){
    digitalWrite(22, LOW);
    digitalWrite(23, HIGH); 
    digitalWrite(24, HIGH); //binary 6
  }
  
  else if(DNo == 7){
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH); 
    digitalWrite(24, HIGH); //binary 7
  }
   else if(DNo == -1){ //make btn displays inactive
    digitalWrite(26, HIGH); //set enable input of multiplexer to inactive
    digitalWrite(25, HIGH); //all pull-up resistors are on HIGH, all displays are inactive
  }

  else if(DNo == -2){ //address all displays at once
    digitalWrite(26, HIGH); //set enable input of multiplexer to inactive, all outputs high-impedance
    digitalWrite(25, LOW); //all pull-up resistors are on LOW, all displays are now CS = active
  }


}


void BtnRunDisplay(uint32_t BCol){ //fill all displays with standard background and lines
                                   //This is performed for all displays at once which saves approx. 200ms per disply
  SetBtnDisplay(-2);  //Address all displays at once
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(65535);

  //Top row
  tft.fillRect(0, 0, 60, 30, 34816); //fill the red top left number area
  tft.fillRect(61, 0, 160, 30, 0); //fill the black area right beside the number area
  tft.drawFastHLine(0, 31, 160, 65535);
  tft.drawFastHLine(0, 64, 160, 65535);
      
  //Cmd Info
  tft.drawFastHLine(0, 109, 160, 65535);
  tft.fillRect(0, 110, 160, 18, TFTCol[27]); //grey bottom line
  tft.setFont(&arial7pt7b);
  //tft.setFont(&ariblk6pt7b);
  
  //Draw 12 rectangles:
  tft.setTextColor(65535);
  for (int N = 0; N < 12; N++){
    int XP = 2 + (N * 13);
    tft.drawRect(XP, 113, 14, 15, 21130); 
    tft.setCursor(XP + 2,124);
    tft.print(RNDLetter()); // Print a series of 12 random letters in each rectangle
  }
  SetBtnDisplay(-1);  //Deactivate Btn Displays
}

void BtnNameDisplay(int BCol){

  BtnNoDisplay(BCol);
  
  int ActDisplay = ((BCol + NrDisplays) % NrDisplays); //Only address 8 physical displays
  SetBtnDisplay(ActDisplay);  //select the actual display

  BGCol = TFTCol[BCol + ColorIndex]; //Select the color from the array variable
  TCol = TFTTCol[BCol + ColorIndex]; //Select the text color

  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(TCol);

   //GRP area
  tft.setFont(&FreeSansBold9pt7b);
  tft.fillRect(0, 32, 160, 32, BGCol); // Background of GRP label section
  tft.setCursor(5, 55);
  tft.print("GRP COLOR ");
  tft.print(BCol + ColorIndex);
  

  //Btn area
  tft.setTextColor(65535);
  tft.setFont(&FreeSansBold12pt7b);
  tft.setCursor(5, 92);
  tft.print("BtnName ");
  tft.print(BCol);
  
  SetBtnDisplay(-1);  //Deactivate Btn Displays

}

void BtnNoDisplay(int No){ //just a demo/test routine printing the Button numbers into the top area of each display
  
if (((No + NrDisplays)% NrDisplays) == 0){ //every 8 btns
  SetBtnDisplay(-2); //address all displays, this saves a lot of time !!!
  tft.fillRect(0, 0, 60, 30, 34816); //fill the top left red area
  tft.fillRect(0, 65, 160, 43, 0); //fill the btn name area black
}

  int ActDisplay = ((No + NrDisplays) % NrDisplays); //Only address 8 physical displays
  SetBtnDisplay(ActDisplay);  //select the actual display

  //tft.fillRect(0, 0, 60, 30, 34816); //need to fill the red area again for printing a new number
  tft.setFont(&FreeSansBold12pt7b);
  tft.setTextColor(65535);
  tft.setCursor(15, 22);
  tft.print(No);
  SetBtnDisplay(-1);  //Deactivate Btn Displays
}

char* RNDLetter(){ // just for demo, delivers a random character
  int z = random(1, 9);
  switch (z){
    case 1:
      return "P";
      break;
    case 2:
      return "C";
      break;
    case 3:
      return "S";
      break;
    case 4:
      return "T";
      break;
    case 5:
      return "O";
      break;
    case 6:
      return "B";
      break;
    case 7:
      return "-";
      break;
    case 8:
      return "/";
      break;
      
  }
}

void MainRunDisplay(int Btn){ // large main display content
  tft2.setTextColor(65535);

  tft2.fillRect(0, 0, 320, 34, 34816); //fill top area red
  tft2.fillRect(0, 37, 320, 203, 0); //fill rest of display black

  tft2.setTextColor(65535);
  tft2.setFont(&FreeSansBold9pt7b);
  tft2.setCursor(180, 20);
  tft2.print("MIDI FACTORY");
  
  if (Btn > 0){
    tft2.setFont(&FreeSansBold12pt7b);
    tft2.setCursor(5, 25);
    tft2.print("Button  ");
    tft2.print(Btn);
  }
}

void MainBtnMenu(int LineCursor, int Mode){ // Shows 12 lines with Command details for a Midi Control Button
//LineCursor = 0 = write only the headline
//LineCursor 1-12 = set or delete a line cursor
//Mode 0 = delete, 1 = set

Serial.print("MainBTnMenu called with LineCursor = ");
Serial.print(LineCursor);
Serial.print("  and Mode =  ");
Serial.println(Mode);

  tft2.setTextColor(TFTCol[31]); // 31 = White
  // tft2.setFont(&arial8pt7b);
  tft2.setFont(&ariblk6pt7b);

  //Headline
  int TSpace = 4;
  int LPos = 0;
  int MenuWd[7] = {44, 59, 59, 29, 39, 39, 39};

  if (LineCursor == 0){ //headline only if called with 0
      for(int n=0; n<7;n++){
          tft2.fillRect(LPos, 36, MenuWd[n], 20, TFTCol[15]);
          tft2.setCursor(LPos + TSpace, 50);
          tft2.print(BtnMainMenu[n]);    
          LPos = LPos + MenuWd[n]+ 2;
      }
  }
  //Cmd numbers
  int LineSpace = 15;
  int FirstLine = 55;
  int LinePos = 0;

  if (LineCursor == 0){ //if called without cursor print entire screen content
      for (int n = 1; n < 13; n++){
          LinePos = FirstLine + (n * LineSpace);      
          tft2.setCursor(10, LinePos);
          tft2.print(n);
          // here is to print all data    
      }
  }
  else { //if called with a cursor 
          LinePos = FirstLine + (LineCursor * LineSpace); //Calculating the vertical postion of a line      
    
          if (Mode == 1){
            tft2.fillRect(0, LinePos-11, 360, 15, TFTCol[28]); //Fill Line grey as a cursor
          }
          else if (Mode == 0){
            tft2.fillRect(0, LinePos-11, 360, 15, 0); //Fill Line black - 'delete' the cursor       
          }
          tft2.setCursor(10, LinePos); //move to the text position
          tft2.print(LineCursor); //print the line number
          // here is all other data in that line to be printed
  }  
} //end MainBnMenu


And here is a photo: