Arduino - TFT LCD Touch Display SPI
This tutorial instructs you how to use an SPI TFT display with Arduino. In detail, we will learn:
- How to wire an SPI TFT display module to an Arduino.
- How to draw shapes, lines, and fill areas with color.
- How to display text and numeric values on screen.
- How to draw bitmap images from program memory (PROGMEM).
- How to draw bitmap images loaded from an SD card.
- How to render text with a custom external font.
- How to read raw touch coordinates from an XPT2046 controller.
- How to draw on screen by dragging a finger across the display.
- How to create interactive touch buttons on screen.
- How to calibrate the touch screen.
- How to use a secondary or custom SPI bus for the display.
This tutorial covers both touch and non-touch SPI TFT LCD displays. It works with 1.3, 1.54, 2.2, 2.4, 2.8, 3.2, and 3.5 inch panels driven by ILI9341, ILI9488, or ST7789 controller chips.

Tip — simpler and faster alternative: If you are using an Arduino Uno, consider the TFT Touch Shield instead. It plugs directly onto the Uno header — no jumper wires required. It uses an 8-bit parallel interface, which transfers pixel data significantly faster than SPI.
Hardware Required
Or you can buy the following kits:
| 1 | × | DIYables STEM V3 Starter Kit (Arduino included) | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some links direct to products from our own brand, DIYables .
About the SPI TFT Display
An SPI TFT display is a full-color LCD panel controlled over the Serial Peripheral Interface (SPI) bus. It uses a dedicated driver chip - most commonly the ILI9341, ILI9488, or ST7789 - to receive drawing commands and manage the pixel data.
Key facts:
- ILI9341 - 16-bit RGB565 color, up to 40 MHz SPI.
- ILI9488 - 18-bit RGB666 color over SPI, up to 24 MHz SPI.
- ST7789 - 16-bit RGB565 color, up to 40 MHz SPI.
Recommendation: If you have not yet purchased a display, we recommend the ST7789 driver. It is widely available, runs at full 40 MHz SPI speed, and is the most straightforward choice for new projects.
Pinout
Most SPI TFT LCD displays have the following pins:
Display pins:
| Pin | Function |
|---|---|
| VCC | Power supply |
| GND | Ground |
| CS | Chip Select — pulled low to select the display on the SPI bus |
| DC / RS | Data / Command select — high for pixel data, low for commands |
| RST | Hardware reset — optional; tie to 3.3V if unused |
| MOSI / SDI / SDA | SPI data in (MCU → display) |
| SCK / CLK | SPI clock |
| MISO / SDO | SPI data out (display → MCU) — optional for display-only use |
| LED / BL / BLK | Backlight power — connect to 3.3V or a PWM pin for dimming |
SD card pins (if your application needs to access the SD card):
| Pin | Function |
|---|---|
| SD_CS / TF_CS | SD card Chip Select |
| MOSI / SDI | MOSI — data from MCU to SD card |
| SCK / CLK | SCK — SPI clock |
| MISO / SDO | MISO — data from SD card to MCU |
For TFT displays that support touch, there are additional touch pins (if your application uses the touch function and the display supports it):
| Pin | Function |
|---|---|
| T_CS | Touch controller Chip Select |
| T_CLK | SCK — SPI clock |
| T_DIN | MOSI — data from MCU to touch controller |
| T_DO | MISO — data from touch controller to MCU |
| T_IRQ | Touch interrupt — optional; signals when the screen is being touched |
Note: Some non-touch display modules also expose T_CS, T_CLK, T_DIN, T_DO, and T_IRQ pins. These are non-functional on those boards — the touch controller IC is not populated. They appear because the PCB reuses the same layout as the touch-enabled version to reduce manufacturing variants.

Wiring Diagram
Note — level converter optional: These SPI TFT display modules include on-board level shifting on their signal lines, so the 5V logic from the Arduino Uno can drive MOSI, SCK, CS, DC, RST, and T_CS (if using touch) directly — this wiring is tested and works. For a permanent build, adding a 5V to 3.3V Level Converter on the MCU-driven signal lines is still the safer choice for long-term reliability. The MISO line (display → MCU) never needs one.
Note — SDO (MISO) is optional: The SDO (MISO) line only carries data from the display back to the board. None of the example code in this tutorial reads from the display, so this pin can be left unconnected. On modules where the touch controller shares the same SPI bus, a connected SDO line can interfere with communication, so leaving it unconnected is recommended.
Without Touch
Connect MOSI to D11, SCK to D13, MISO to D12 on the Arduino Uno. CS, DC, and RST can be any available GPIO — D10, D8, D9 are used in the examples.
Display:
| TFT Pin | Arduino Uno Pin | Description |
|---|---|---|
| VCC | 3.3V | Power supply (3.3V only — the display runs at 3.3V) |
| GND | GND | Ground |
| CS | D10 | Chip Select |
| DC / RS | D8 | Data / Command select |
| RST | D9 | Reset (optional) |
| MOSI / SDI | D11 | Hardware SPI MOSI |
| SCK | D13 | Hardware SPI clock |
| MISO / SDO | D12 | Hardware SPI MISO (optional) |
| LED / BL | 3.3V | Backlight power |
SD card (if your application needs to access the SD card):
| SD Pin | Arduino Uno Pin | Description |
|---|---|---|
| SD_CS / TF_CS | any free GPIO | SD card Chip Select |
| MOSI / SDI | D11 | Shared with display MOSI (D11) |
| SCK / CLK | D13 | Shared with display SCK (D13) |
| MISO / SDO | D12 | Shared with display MISO (D12) |

This image is created using Fritzing. Click to enlarge image
With Touch
Connect the XPT2046 touch controller to the Arduino Uno SPI bus, sharing D11, D13, and D12 with the display.
Display:
| TFT Pin | Arduino Uno Pin | Description |
|---|---|---|
| VCC | 3.3V | Power supply (3.3V only — the display runs at 3.3V) |
| GND | GND | Ground |
| CS | D10 | Chip Select |
| DC / RS | D8 | Data / Command select |
| RST | D9 | Reset (optional) |
| MOSI / SDI | D11 | Hardware SPI MOSI |
| SCK | D13 | Hardware SPI clock |
| MISO / SDO | D12 | Hardware SPI MISO (optional) |
| LED / BL | 3.3V | Backlight power |
Touch controller (if your application uses the touch function and the display supports it):
| Touch Pin | Arduino Uno Pin | Description |
|---|---|---|
| T_CS | any free GPIO | Touch Chip Select |
| T_IRQ | any free GPIO | Touch interrupt (optional) |
| T_DIN | D11 | Shared with display MOSI (D11) |
| T_CLK | D13 | Shared with display SCK (D13) |
| T_DO | D12 | Shared with display MISO (D12) |

This image is created using Fritzing. Click to enlarge image
If your MCU has two or more hardware SPI interfaces, you can assign each peripheral (display, SD card, touch controller) to its own dedicated SPI bus. If your MCU has only one hardware SPI interface, all three peripherals share the same three data lines (MOSI, SCK, MISO) — on the Uno these are D11, D13, and D12. Each peripheral has its own CS pin, so only one is active at a time. The DIYables_TFT_SPI library manages both the display and the XPT2046 touch controller through a single API — no separate SPI library is needed for the touch side.
Library Installation
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port.
- Navigate to the Libraries icon on the left bar of the Arduino IDE.
- Search "DIYables_TFT_SPI", then find the DIYables_TFT_SPI library by DIYables.
- Click Install button to install the latest version of the library.
- When prompted to install dependencies, click Install All to also install the Adafruit GFX Library.
- Search for DIYables TFT SPI created by DIYables.io and click the Install button.
Basic Structure
Every sketch using the DIYables_TFT_SPI library follows this basic structure:
The width and height passed to the constructor must match the physical resolution printed on your module's datasheet. Modules using the same driver chip are sold in different sizes - for example, ST7789 modules come in 240x320, 240x240, 135x240, and other variants.
Arduino Code - Draw Shapes
The DrawShapes example shows how to draw circles, triangles, rectangles, rounded rectangles, and lines using the built-in Adafruit GFX drawing functions.
Quick Steps
- Wire the TFT display to the Arduino following the wiring diagram above.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port.
- Copy the above code and paste it to the editor of Arduino IDE.
- Change the constructor line to match your display driver and resolution.
- Click Upload button on Arduino IDE to upload code to Arduino.
- The display shows a pattern of colored shapes that fills and redraws every few seconds.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.begin() | Initialize the display. Call once in setup(). | TFT_display.begin(); |
| TFT_display.setRotation(r) | Set screen orientation. 0=portrait, 1=landscape. | TFT_display.setRotation(1); |
| TFT_display.fillScreen(color) | Fill entire screen with one color. | TFT_display.fillScreen(BLACK); |
| DIYables_TFT_SPI::colorRGB(r,g,b) | Convert RGB values to a 16-bit color. | colorRGB(255, 0, 0) |
| TFT_display.drawCircle(x,y,r,color) | Draw a circle outline. | TFT_display.drawCircle(60, 60, 30, RED); |
| TFT_display.fillCircle(x,y,r,color) | Draw a filled circle. | TFT_display.fillCircle(60, 60, 30, RED); |
| TFT_display.drawRect(x,y,w,h,color) | Draw a rectangle outline. | TFT_display.drawRect(10, 10, 80, 40, BLUE); |
| TFT_display.fillRect(x,y,w,h,color) | Draw a filled rectangle. | TFT_display.fillRect(10, 10, 80, 40, BLUE); |
| TFT_display.drawLine(x0,y0,x1,y1,color) | Draw a line. | TFT_display.drawLine(0, 0, 100, 100, GREEN); |
Arduino Code - Show Text and Number
The ShowTextAndNumber example demonstrates how to print text strings and numeric values on the display at different sizes and positions.
Quick Steps
- Wire the display and upload the code as described above.
- The display prints several lines of text with different colors and sizes.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.setTextColor(color) | Set the text foreground color. | TFT_display.setTextColor(WHITE); |
| TFT_display.setTextSize(size) | Set text scale (1=6x8 px, 2=12x16 px, ...). | TFT_display.setTextSize(2); |
| TFT_display.setCursor(x, y) | Move the text cursor to a position. | TFT_display.setCursor(10, 20); |
| TFT_display.print(value) | Print a string or number at the cursor. | TFT_display.print("Hello!"); |
| TFT_display.println(value) | Print and move cursor to next line. | TFT_display.println(42); |
Arduino Code - Draw Image
The DrawImage example displays a full-color RGB565 bitmap stored in program memory (PROGMEM). The pixel data lives in a companion bitmap.h header file as a const uint16_t array and is never copied into SRAM, making it suitable for memory-constrained boards. Add bitmap.h to the same folder as the sketch before compiling.
Quick Steps
- Wire the TFT display to the Arduino following the wiring diagram above.
- Place bitmap.h in the same folder as the sketch.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- The display shows the bitmap image stored in program memory.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.drawRGBBitmap(x,y,bitmap,w,h) | Draw an RGB565 bitmap from PROGMEM at position (x, y). The array must be declared PROGMEM. | TFT_display.drawRGBBitmap(0, 0, myImage, 240, 320); |
| TFT_display.fillScreen(color) | Clear the screen to a solid color before drawing the bitmap. | TFT_display.fillScreen(BLACK); |
Arduino Code - Draw Image SD Card
The DrawImageSDcard example reads a raw RGB565 binary image file directly from a micro SD card and streams pixel data to the display in chunks, avoiding the need to load the full image into RAM. Connect an SD card module to the same SPI bus as the display and define its CS pin as SD_CS_PIN in the sketch.
Quick Steps
- Wire the SD card module to the Arduino. Share the MOSI (D11), SCK (D13), and MISO (D12) lines with the display. Connect the SD module CS to the pin defined as SD_CS_PIN.
- Copy a raw RGB565 binary image to the root of the SD card. The file dimensions must match the panel's width and height.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- The display shows the image streamed from the SD card.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.startWrite() | Begin a direct SPI transaction. Keeps CS asserted for the duration. | TFT_display.startWrite(); |
| TFT_display.setAddrWindow(x0,y0,x1,y1) | Set the rectangular region of pixels to write. | TFT_display.setAddrWindow(0, 0, 239, 319); |
| TFT_display.pushColors(buf, len) | Stream a buffer of RGB565 pixel words to the display. | TFT_display.pushColors(buf, 512); |
| TFT_display.endWrite() | End the SPI transaction and release CS. | TFT_display.endWrite(); |
Arduino Code - Use External Font
The UseExternalFont example renders text using an Adafruit GFX-compatible custom font for sharper, higher-quality glyphs. The font is included as a header file and activated by calling setFont(). Pass NULL to revert to the built-in 5×7 pixel font at any time.
Quick Steps
- Wire the TFT display to the Arduino following the wiring diagram above.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- The display renders text in the custom font. Compare it to the built-in font to see the improvement in sharpness.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.setFont(&FontName) | Switch to a custom GFX-compatible font. Pass NULL to restore the built-in 5×7 font. | TFT_display.setFont(&FreeSans12pt7b); |
| TFT_display.setCursor(x, y) | Position the text cursor before calling print(). | TFT_display.setCursor(10, 40); |
| TFT_display.setTextColor(color) | Set the text foreground color. | TFT_display.setTextColor(WHITE); |
| TFT_display.print(text) | Print a string using the active font. | TFT_display.print("Hello!"); |
Arduino Code - Touch Get Point
The TouchGetPoint example initializes the XPT2046 touch controller and reads raw ADC coordinates whenever the screen is touched. Raw values are printed to the Serial Monitor. Run this example first to understand the numeric range of your panel before applying calibration.
Wiring: connect T_CLK, T_DIN, and T_DO to the same SPI pins as the display (D13, D11, D12). Connect T_CS to pin 7 and T_IRQ to pin 6. The XPT2046 shares the SPI bus with the display.
Quick Steps
- Wire the XPT2046 touch controller to the Arduino. T_CLK→D13, T_DIN→D11, T_DO→D12 (shared with display). T_CS→D7, T_IRQ→D6.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- Open the Serial Monitor at 9600 baud. Touch the screen to see raw X, Y, and pressure Z values printed.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.initTouchSPI(cs, irq) | Initialize the XPT2046 on the shared SPI bus. Pass -1 for irq if the pin is not connected. | TFT_display.initTouchSPI(7, 6); |
| TFT_display.readTouchRaw(x, y, z) | Read raw ADC values from the touch controller. Returns true when the screen is being touched. | TFT_display.readTouchRaw(x, y, z); |
Arduino Code - Touch Draw
The TouchDraw example turns the display into a finger-painting canvas. As you drag a finger across the screen, colored dots are placed at each touch coordinate using calibrated positions, creating a continuous drawn line.
Quick Steps
- Wire the XPT2046 touch controller to the Arduino as described in the Touch Get Point section above.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- Drag a finger across the display to draw on screen. Lift and drag again to start a new stroke.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.initTouchSPI(cs, irq) | Initialize the XPT2046 on the shared SPI bus. | TFT_display.initTouchSPI(7, 6); |
| TFT_display.setTouchCalibration(minX,maxX,minY,maxY) | Map raw ADC values to screen pixel coordinates. Obtain the four values from the TouchCalibration example. | TFT_display.setTouchCalibration(200, 3800, 300, 3700); |
| TFT_display.setTouchInvertX(invert) / TFT_display.setTouchInvertY(invert) | Flips the touch axis when X or Y is mirrored on your specific panel or batch. Call BEFORE setTouchCalibration(). | TFT_display.setTouchInvertY(true); |
| TFT_display.getTouch(x, y) | Get calibrated touch position in screen pixels. Returns true when the screen is being touched. | if (TFT_display.getTouch(x, y)) { ... } |
| TFT_display.fillCircle(x, y, r, color) | Draw a filled dot at the touch position. | TFT_display.fillCircle(x, y, 3, RED); |
Arduino Code - Touch Calibration
The TouchCalibration example walks you through finding the correct calibration values for your specific XPT2046 panel. Touch the corners of the screen when prompted and read the min/max X and Y values from the Serial Monitor. Copy those four numbers into setTouchCalibration() in all other touch examples.
Quick Steps
- Wire the XPT2046 touch controller to the Arduino as described in the Touch Get Point section.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- Open the Serial Monitor at 9600 baud. Touch each corner of the screen as instructed.
- Note the four printed values (minX, maxX, minY, maxY) and paste them into setTouchCalibration() in your other touch sketches.
API Summary
| Method | Description | Example |
|---|---|---|
| TFT_display.initTouchSPI(cs, irq) | Initialize the XPT2046 touch controller. | TFT_display.initTouchSPI(7, 6); |
| TFT_display.readTouchRaw(x, y, z) | Read raw ADC values used to determine the calibration range. | TFT_display.readTouchRaw(x, y, z); |
| TFT_display.setTouchCalibration(minX,maxX,minY,maxY) | Store calibration constants so getTouch() maps raw values to pixel coordinates. | TFT_display.setTouchCalibration(200, 3800, 300, 3700); |
| TFT_display.setTouchInvertX(invert) / TFT_display.setTouchInvertY(invert) | Flips the touch axis when X or Y is mirrored on your specific panel or batch. Call BEFORE running calibration so the stored values match your panel. | TFT_display.setTouchInvertY(true); |
Arduino Code - Custom SPI
The CustomSPI example demonstrates how to pass an explicit SPIClass pointer to the display constructor. This matters when another peripheral already occupies the default SPI bus or when you need to limit the SPI clock speed for long or noisy cables.
The Arduino Uno has one hardware SPI bus on D11 (MOSI), D13 (SCK), and D12 (MISO). The example shows how to pass &SPI explicitly and how to configure a lower maximum SPI speed.
Quick Steps
- Wire the TFT display to the Arduino as shown in the wiring diagram.
- Connect the Arduino board to your computer with a USB cable.
- Open Arduino IDE, select the right board and port, paste the code, and click Upload.
- The display initializes on the explicit SPI bus and shows a color-bar test pattern.
API Summary
| Method | Description | Example |
|---|---|---|
| DIYables_ILI9341_SPI(w,h,cs,dc,rst,spi) | Constructor accepting an explicit SPIClass pointer as the last argument. Omit it to default to &SPI. | DIYables_ILI9341_SPI tft(240, 320, 10, 9, 8, &SPI); |
| TFT_display.begin() | Initialize the display on the configured SPI bus. | TFT_display.begin(); |
Troubleshoot
If the code is not working, there are some common issues you can troubleshoot:
- Black screen: Check that VCC, GND, CS, DC, MOSI, and SCK are all wired correctly. A missing CS or DC wire is the most common cause.
- Wrong driver: Make sure you uncommented the constructor line that matches your module's driver chip (ILI9341, ILI9488, or ST7789). A mismatched driver will show a blank or all-white screen.
- Wrong resolution: The width and height passed to the constructor must match your panel. An incorrect size causes the image to be shifted or clipped.
- Garbage on screen: Verify MOSI and SCK are on the hardware SPI pins (D11 and D13 on Uno). Swapping them is a common mistake.
- Touch not responding: Run the TouchCalibration example to find the correct calibration values for your panel.
Platform Support
The DIYables_TFT_SPI library uses standard Arduino SPI APIs and supports all Arduino-compatible platforms (architectures=*). The Adafruit GFX Library dependency is also cross-platform.