Arduino UNO R4 - TFT LCD Touch Display SPI
The Arduino Uno R4 - available as Minima and WiFi - brings a 48 MHz Renesas RA4M1 processor and more SRAM than the classic Uno, while keeping the same header layout. The hardware SPI pins are in the same physical locations, so any TFT wiring that works on the Uno R3 works directly on the Uno R4 without any changes.
In this guide you will:
- Wire an SPI TFT display to the Arduino Uno R4.
- Draw shapes and fill areas with color.
- Display text and numbers on screen.
- Draw bitmap images stored in program memory (PROGMEM).
- Draw bitmap images loaded from an SD card.
- Render text using a custom external font.
- Read raw touch coordinates from an XPT2046 controller.
- Draw on screen by dragging a finger across the display.
- Create interactive touch buttons.
- Calibrate the touch screen.
- Use a secondary or custom SPI bus for the display.
This guide 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 R4, consider the TFT Touch Shield instead. It plugs directly onto the Uno R4 header — no jumper wires required. It uses an 8-bit parallel interface, which transfers pixel data significantly faster than SPI.
Hardware Preparation
Or you can buy the following kits:
| 1 | × | DIYables STEM V4 IoT Starter Kit (Arduino included) | |
| 1 | × | DIYables STEM V4B IoT Starter Kit (Arduino included) | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some of these links are for products from our own brand, DIYables .
SPI TFT Display Overview
An SPI TFT display is a color LCD panel driven by a controller chip over the SPI bus. The DIYables_TFT_SPI library supports three common driver chips:
- ILI9341 - 16-bit RGB565 color, up to 40 MHz SPI.
- ILI9488 - 18-bit RGB666 color, 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.
The library extends Adafruit GFX, so all standard drawing and text functions are available.
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 R4 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 Uno R4. CS, DC, and RST can be any available GPIO — D10, D8, D9 are used in the examples.
Display:
| TFT Pin | Arduino Uno R4 Pin | Description |
|---|---|---|
| VCC | 3.3V or 5V | Power supply |
| 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 R4 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 R4 SPI bus, sharing D11, D13, and D12 with the display.
For a project that lasts, screw terminals hold the wires better than jumper wires. A Screw Shield V2 or a Proto Screw Terminal Block Shield solves this. Plug it into the Arduino UNO R4 and screw the wires in. Here is the same circuit wired through the Screw Shield V2:

This image is created using Fritzing. Click to enlarge image
Display:
| TFT Pin | Arduino Uno R4 Pin | Description |
|---|---|---|
| VCC | 3.3V or 5V | Power supply |
| 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 R4 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 R4 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.
Do you want the wiring to stay in place? To do that, mount a Proto Screw Terminal Block Shield or a Screw Shield V2 on the Arduino UNO R4. See the diagram below for the screw shield version.

This image is created using Fritzing. Click to enlarge image
See The best way to supply power to the Arduino Uno R4 and other components.
How to Install the Library
- Plug the Arduino Uno R4 into your computer using a USB Type-C cable.
- In the Arduino IDE, verify that the correct board (Arduino Uno R4 Minima or WiFi) and serial port are selected.
- Click the Libraries icon in the left sidebar.
- Type "DIYables_TFT_SPI" into the search box and locate the library published by DIYables.
- Hit Install to add the library. When prompted, also install the Adafruit GFX Library dependency.
- Search for DIYables TFT SPI created by DIYables.io and click the Install button.
Minimal Sketch Structure
All sketches using the DIYables_TFT_SPI library share this skeleton:
Pass the physical pixel dimensions of your panel to the constructor. Modules with the same driver chip come in different sizes.
Draw Shapes
The DrawShapes example demonstrates the Adafruit GFX drawing primitives: circles, triangles, rectangles, rounded rectangles, and lines.
How to Run
- Wire the TFT module to the Arduino Uno R4 as shown in the wiring diagram.
- Plug in the USB Type-C cable.
- Paste the code in Arduino IDE, choose the correct board and port, and press Upload.
- The display shows a grid of colored shapes that refreshes every few seconds.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| begin() | Initialize the display controller. Call once in setup(). | TFT_display.begin(); |
| setRotation(r) | Rotate the coordinate system. 0=portrait, 1=landscape. | TFT_display.setRotation(1); |
| fillScreen(color) | Clear screen with a solid color. | TFT_display.fillScreen(WHITE); |
| colorRGB(r,g,b) | Build a 16-bit RGB565 color from 8-bit components. | colorRGB(0, 128, 255) |
| fillCircle(x,y,r,color) | Draw a solid circle. | TFT_display.fillCircle(80, 80, 40, RED); |
| fillRect(x,y,w,h,color) | Draw a solid rectangle. | TFT_display.fillRect(10, 10, 60, 40, BLUE); |
| drawRoundRect(x,y,w,h,r,color) | Draw a rounded rectangle outline. | TFT_display.drawRoundRect(5,5,100,50,10,GREEN); |
Show Text and Number
The ShowTextAndNumber example shows how to print strings and numbers at different sizes, colors, and positions.
How to Run
- Wire and upload as above.
- The display prints several lines of text and numeric values.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| setTextColor(color) | Set text foreground color. | TFT_display.setTextColor(BLACK); |
| setTextSize(size) | Scale text. Size 1 is 6x8 pixels. | TFT_display.setTextSize(2); |
| setCursor(x, y) | Position the text cursor. | TFT_display.setCursor(10, 30); |
| print(value) | Print a string or number at the cursor. | TFT_display.print("Temp: "); |
| println(value) | Print and advance to next line. | TFT_display.println(25.4); |
Draw Image
The DrawImage example displays a full-color RGB565 bitmap image stored in program memory. The image data is declared in bitmap.h as a const uint16_t array marked PROGMEM. This keeps the pixel data in flash rather than SRAM, so the display works correctly even on boards with limited RAM. Copy bitmap.h into the sketch folder before compiling.
How to Run
- Place bitmap.h in the same folder as the sketch file.
- Wire the TFT module to the Arduino Uno R4 as shown in the wiring diagram.
- Plug in the USB Type-C cable.
- Paste the code in Arduino IDE, confirm the board and port, and press Upload.
- The display shows the bitmap image loaded from program memory.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| drawRGBBitmap(x,y,bitmap,w,h) | Draw an RGB565 PROGMEM bitmap starting at pixel (x, y). | TFT_display.drawRGBBitmap(0, 0, myImage, 240, 320); |
| fillScreen(color) | Clear the screen before drawing the image. | TFT_display.fillScreen(BLACK); |
Draw Image SD Card
The DrawImageSDcard example reads a raw RGB565 binary image file from a micro SD card and pushes pixel data directly to the display in small chunks. This approach streams the image without ever holding the full image in RAM, making it practical for large panels.
Connect the SD card module to the same SPI bus as the display. Share MOSI (D11), SCK (D13), and MISO (D12). Assign a separate CS pin to the SD module — define it as SD_CS_PIN in the sketch.
How to Run
- Wire the SD module to the Arduino Uno R4. Share the SPI bus (D11/D13/D12) with the display and connect SD CS to your chosen pin.
- Copy a raw RGB565 binary image file to the root of the SD card. Image dimensions must match the panel resolution.
- Plug in the USB Type-C cable.
- Paste the code in Arduino IDE, confirm board and port, and press Upload.
- The image is read from the card and displayed on screen.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| startWrite() | Begin a direct SPI transaction, holding CS asserted. | TFT_display.startWrite(); |
| setAddrWindow(x0,y0,x1,y1) | Define the rectangular pixel region to write. | TFT_display.setAddrWindow(0, 0, 239, 319); |
| pushColors(buf, len) | Send a buffer of RGB565 pixel values to the display. | TFT_display.pushColors(buf, 512); |
| endWrite() | End the SPI transaction and release CS. | TFT_display.endWrite(); |
Use External Font
The UseExternalFont example renders on-screen text using a custom Adafruit GFX-compatible font. Compared to the built-in 5×7 bitmap font, custom fonts provide better readability and styling. The font file is included as a header and activated with a single call to setFont().
How to Run
- Wire the TFT module to the Uno R4 as shown in the wiring diagram.
- Plug in the USB Type-C cable.
- Paste the code in Arduino IDE, confirm board and port, and press Upload.
- The display renders text in the custom font. Notice the improved glyph quality compared to the built-in font.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| setFont(&FontName) | Activate a custom GFX-compatible font. Pass NULL to restore the built-in 5×7 font. | TFT_display.setFont(&FreeSans12pt7b); |
| setCursor(x, y) | Move the text cursor to the given position. | TFT_display.setCursor(10, 40); |
| setTextColor(color) | Set the text foreground color. | TFT_display.setTextColor(WHITE); |
| print(text) | Print a string at the current cursor position using the active font. | TFT_display.print("Hello!"); |
Touch Get Point
The TouchGetPoint example reads raw ADC coordinates from an XPT2046 touch controller and prints them to the Serial Monitor. Running this first helps you understand the numeric range of your specific panel — data you will use to calibrate the touch in later examples.
Wiring: T_CLK→D13, T_DIN→D11, T_DO→D12 (shared SPI bus with display). T_CS→D7, T_IRQ→D6.
How to Run
- Wire the XPT2046 controller to the Uno R4, sharing the SPI bus with the display. T_CS to D7, T_IRQ to D6.
- Plug in the USB Type-C cable.
- Paste the code, confirm board and port, and press Upload.
- Open the Serial Monitor at 9600 baud. Press and drag on the display to see raw X, Y, and pressure values.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| initTouchSPI(cs, irq) | Initialize the XPT2046 on the shared SPI bus. Use -1 for irq if the interrupt pin is not connected. | TFT_display.initTouchSPI(7, 6); |
| readTouchRaw(x, y, z) | Return raw ADC touch values without applying any calibration. Returns true when pressed. | TFT_display.readTouchRaw(x, y, z); |
Touch Draw
The TouchDraw example creates a simple finger-drawing application. Calibrated touch coordinates are used to place small filled circles at each point the finger contacts, building up a continuous drawing as it moves.
How to Run
- Wire the XPT2046 to the Uno R4 as described in the Touch Get Point section above.
- Plug in the USB Type-C cable.
- Paste the code, confirm board and port, and press Upload.
- Drag a finger across the display to paint on screen.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| initTouchSPI(cs, irq) | Set up the XPT2046 on the shared SPI bus. | TFT_display.initTouchSPI(7, 6); |
| setTouchCalibration(minX,maxX,minY,maxY) | Map raw ADC values to screen pixel coordinates. Get these values from the TouchCalibration example. | TFT_display.setTouchCalibration(200, 3800, 300, 3700); |
| setTouchInvertX(invert) / 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); |
| getTouch(x, y) | Retrieve calibrated touch coordinates in screen pixels. Returns true when the screen is being pressed. | if (TFT_display.getTouch(x, y)) { ... } |
| fillCircle(x, y, r, color) | Draw a small dot at the touch point. | TFT_display.fillCircle(x, y, 3, RED); |
Touch Calibration
The TouchCalibration example guides you through measuring the raw ADC range of your XPT2046 touch panel. Touch each corner when prompted and observe the minimum and maximum X and Y values printed to the Serial Monitor. Use those four numbers in the setTouchCalibration() call throughout all your other touch sketches.
How to Run
- Wire the XPT2046 to the Uno R4 as described in the Touch Get Point section.
- Plug in the USB Type-C cable.
- Paste the code, confirm board and port, and press Upload.
- Open the Serial Monitor at 9600 baud. Follow the on-screen prompts to touch each corner of the display.
- Record the four printed values and insert them into setTouchCalibration() in your other touch sketches.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| initTouchSPI(cs, irq) | Initialize the XPT2046 touch controller. | TFT_display.initTouchSPI(7, 6); |
| readTouchRaw(x, y, z) | Read raw ADC values to measure the calibration range. | TFT_display.readTouchRaw(x, y, z); |
| setTouchCalibration(minX,maxX,minY,maxY) | Store the calibration values so getTouch() maps correctly to pixel coordinates. | TFT_display.setTouchCalibration(200, 3800, 300, 3700); |
| setTouchInvertX(invert) / 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); |
Custom SPI
The CustomSPI example shows how to supply an explicit SPIClass instance to the display constructor. Use this approach when the default SPI bus is busy with another peripheral, when you need to share the bus with additional configuration, or when you want to set a custom SPI clock frequency.
The Uno R4 Minima and WiFi each expose a single hardware SPI bus on D11/D13/D12. The example demonstrates passing &SPI explicitly and adjusting the maximum SPI clock speed.
How to Run
- Wire the TFT display to the Uno R4 as shown in the wiring diagram.
- Plug in the USB Type-C cable.
- Paste the code, confirm board and port, and press Upload.
- The display starts up on the explicit SPI bus and renders a color-bar test pattern.
Method Quick Reference
| Method | What It Does | Usage |
|---|---|---|
| DIYables_ILI9341_SPI(w,h,cs,dc,rst,spi) | Constructor variant that accepts a pointer to any SPIClass. Defaults to &SPI when the argument is omitted. | DIYables_ILI9341_SPI tft(240, 320, 10, 9, 8, &SPI); |
| begin() | Initialize the display on the configured SPI bus. | TFT_display.begin(); |
Troubleshooting
- Black screen - Confirm CS, DC, MOSI, and SCK are all connected. A missing CS line leaves the display inactive.
- Wrong driver - Only one constructor line should be active. The others must be commented out.
- Shifted or clipped image - The width and height in the constructor must match the physical panel size.
- Touch not working - Run the TouchCalibration example to obtain calibration values specific to your panel.
Platform Support
The library relies exclusively on Arduino standard APIs (SPI, digitalWrite, millis) and runs on all Arduino-compatible platforms including the Uno R4 Minima and WiFi.