Using multiple ili9341 screens with tft_espi library

Ever wanted to run more than one screen independently? Are you using the excellent tft_espi library? Well this guide will walk you through the small bit of work you need to do to achieve this, in particular with an ESP32 but the principles can be applied to any MCU of your choice.

Now, disclaimer. This library was not conceived to run with more than one screen and so although the work required is minimal it is slightly more than what would have been required had the library been designed from the ground up with this in mind.

I’ve produced a video that goes through the main points outlined here if that kind of thing spins your bottle!

Pre-requisite Reading/ watching.
In this article and video above I go through the connections you need for the two screen set up but nothing more. You should watch the video below for more detail and for the information on installing the library. It is important that you install the library version mentioned in the video, changes in the later library will cause the code not to compile.

IMPORTANT – So that’s worth mentioning again as it causes the most support messages I get. You must install version 2.2.0 of the library, other versions may cause the code not to compile.

Here’s the previous version which just talks about getting a single screen up and running;

Principle of running multiple devices on the SPI bus.

The SPI bus is as the name suggests is a – bus -. Wires that are designed to have multiple devices connected to them to exchange information to whatever device deals with that data. But you can’t have more than one device using the bus at once so the Bus Master or more usually the CPU we are using controls which device uses the bus. To do this every SPI device has a “chip select” pin – or perhaps a more accurate name in this scenario would be “device select”. Whichever, when this pin is set to low then the device can use the bus, if high then it cannot use the bus. Look at the diagram below;

When the CPU wants to send data to screen 1 it makes the chips select on I/O 10 to low and ensures that I/O 11, the select for screen 2 is high. We can now send whatever we want to screen 1 and screen 2 will happily ignore it. If we want to write to screen 2 then we set 11 low and set 10 high. Etc.

Now, with these screens because they only ever passively listen to the bus we could actually send the same data to both screens and get them to both display the same data.

The trouble with the tft_espi library

The tft_espi library requires you to set which pin is the “Chip Select” pin in a settings header file. The library will then set and unset this pin as it sees fit as it’s writing data to the screen. So as much as you might want to use this pin for one of the screens you can’t as you need to explicitly control when it is low or high and unfortunately the library will ignore you and set this pin itself no matter that you might want. So we still need to tell the library to use a certain pin but we will then ignore this and use whatever other pins we want to use for the chip select so that we can control them explicitly. Generally the default for the library is pin 5 on an ESP32 so we’ll leave that as it is. This means that oin 5 cannot be used for any other use.

The connections

I’ve covered connecting up these screens before in this ARTICLE so I won’t go into to much detail. Refer to the circuit diagram below and the picture of the breadboard circuit.

Power ground and LED backlight are connected as you would expect. The screen RESETs are both connected together and go to pin D4 on the ESP32. The DC’s are connected together also and go to pin D2. I think this stands for Data Control, could be wrong, not bothered to look it up. But it tells the screen if the data arriving is for the registers or the actual screen display. The MOSI connection of the SPI bus is the connection that the screen uses to receive data, we can tie these together too and connect to D23. The clock’s SCK are also tied together and go to pin D18.

Ok, so here we are at the most important pins for our task, the Chip Selects of the two screens. Screen one goes to D22 and screen 2 to D21. Notice that pin 5 is not connected to anything but will be used by the library and so we cannot use it for any purpose. So by changing pins 21 and 22 we can control which screen our code and thus the library talks to. Let’s look at some examples.

Test Code – Write to both screens at once

Let’s take possibly the simplest example we could (least I think so) and write to two screens at once.

Now even though the screens are a different size they are using the same driver (ili9341) and have the same resolution (320 x 240) so behave identically. Writing to different screens of different drivers or resolutions is beyond the scope of this article.

It can be seen that we set both chip select lines to low (0v) and just write the code as if we were writing to one screen.

Writing different things to different screens

The, still, simple code sets both CS’s to low first as the initialisation part of the code is the same for both screens. So by doing this both will receive the initialisation data. We then turn off ( set its CS to high) the small screen from the bus so it ignores everything from now on and write the phrase “Large screen” to the SPI bus. As only the large screen CS is currently low then that is the only screen to write this to it’s display. We then enable the small screen be setting its CS low and disable the large screen by setting its CS to high and then write the small screen text.

And in essence that’s it. That’s all you need to do, whether it’s two screens or several. Just control the CS lines as appropriate and make sure you don’t use the CS line assigned to be used by the library for anything (in out case pin 5). You can use any (as far as I’ve tested) routines in the library and all should be good.

Advanced demo

The more advanced demo in the video is available to download below but due to it’s length I won’t be going through it here. But in principle it’s just the same, if you look carefully you can see where I set the CS pin and then call one routine for one screen and then set the other CS and call the other routine. This demo is an amalgamation of two demos supplied with the library. It was not straight forward to combine them simply because they were not written to be independent of each other. There were variable name clashes and one of the routines didn’t release from the main loop once entered. So a little rewrite was required. But all these problems were unrelated to using dual screens. I also created some handy routines for setting the CS’s for this setup that made the code more readable.

Demo 3

You don’t get something for nothing

If you think your going to write a dual screen game, maybe a remake of the Donkey King Game and Watch then beware. You are now writing twice as much data to the screens so half the speed. Of course depending on how you design your code this might not be a problem. Careful and clever coding, only updating what is strictly necessary, can make a tremendous difference but that’s for you to discover as you go along 🙂

Hope this has been helpful, if it has please consider supporting the site/channel by donating in the box at the top right of every page or by the was described on the video. Things are tight for all at the moment though, so if you’ve just enjoyed the video/information then that is reward enough!

3 Comments

  1. This is just nonsense, the solution should be to fix the library so that each screen can have its own handle (own object – as in a class instantiation at runtime). Surely, this has been taught in CS101 nowadays???

    • Sorry for late reply, for a few months now my site is being plagued with spam for unsavoury sites and it takes time to run through the comments these days so I put it off for a while.
      I don’t know the Author, not sure what their reasons were for doing this way, but I agree it would be much better to create new instances.

  2. Hi Xtronics,
    Your sketches work fines as well with my brand new 1.69 inch 280×240 SPI TFT’s with curved corners connected to an ESP32 The controller is a ST7789A. A little bit of tinkering with the ST7749 User_Setup.h and off one goes.

Comments are closed.