Skip to content
FGCV / Engineering Notes

How to connect a 2.8 inch TFT display module to Arduino?

admin By the admin

How to connect a 2.8 inch TFT display module to Arduino

To connect a 2.8 inch tft display module for arduino, you need to wire it using SPI communication, which is the most common interface for these modules. The 2.8-inch TFT display typically uses the ILI9341 driver, runs at 240x320 resolution, and operates at 3.3V logic levels, though many boards include a 5V compatible regulator. Start by connecting the VCC pin to 5V on your Arduino Uno or Mega, and GND to ground. The SPI pins: CS (chip select) to digital pin 10, DC (data/command) to pin 9, RESET (reset) to pin 8, MOSI (master out slave in) to pin 11, MISO (master in slave out) to pin 12, and SCK (serial clock) to pin 13. For the backlight LED, connect it to 3.3V through a 100-ohm resistor to limit current, or use a PWM pin on the Arduino for brightness control. The SD card slot on the module, if present, uses separate SPI lines: CS to pin 4, MOSI to pin 11, MISO to pin 12, SCK to pin 13. This wiring is based on the standard SPI pinout on Arduino Uno, and you can verify it with a multimeter before powering up.

Now, let’s dive into the hardware specifics. The 2.8-inch TFT module from DisplayModule (model DM-TFT28-105) uses the ILI9341 controller, which supports 16-bit color depth (65k colors) and a refresh rate of up to 60 Hz when driven by a 16 MHz Arduino. The module draws about 80-120 mA with the backlight on, so it’s safe to power directly from the Arduino’s 5V pin, which can supply up to 500 mA on a USB-powered board. However, if you’re using an Arduino Nano or Pro Mini, the voltage regulator might overheat if you add other peripherals, so consider an external 5V supply. The SPI clock speed is critical: the ILI9341 can handle up to 40 MHz, but Arduino Uno’s SPI library defaults to 4 MHz, which is fine for static images. For animations, you can push it to 8 MHz by setting SPI.setClockDivider(SPI_CLOCK_DIV2) in your code, but keep the wires short (under 20 cm) to avoid signal degradation. The module’s touch screen, if resistive, uses an XPT2046 controller on SPI, with its own CS pin (usually pin 6 on the module). You’ll need to wire that separately: T_IRQ to pin 7, T_DO to pin 12, T_DIN to pin 11, T_CS to pin 6, T_CLK to pin 13. This adds two more pins, but the Arduino Uno has enough digital I/O.

For software, you need the Adafruit ILI9341 library and the Adafruit GFX library, both available in the Arduino Library Manager. Install them via Sketch > Include Library > Manage Libraries, then search for “Adafruit ILI9341” and “Adafruit GFX”. The library handles the low-level SPI commands, but you must define the pins in your code. Here’s a typical setup for the Uno: Adafruit_ILI9341 tft = Adafruit_ILI9341(10, 9, 8); where 10 is CS, 9 is DC, and 8 is RESET. If you’re using the touch screen, add the XPT2046 library (e.g., from Paul Stoffregen) and define its pins. The touch screen’s resolution is 4096x4096, but you’ll need to calibrate it to the 240x320 display by mapping the raw values. A common calibration routine reads the touch coordinates at four corners and applies a linear transformation. The SPI bus for the touch screen shares the same MOSI, MISO, and SCK lines, so you can use the same hardware SPI interface, but the CS pin must be toggled separately to avoid conflicts.

Let’s talk about power management and heat. The 2.8-inch TFT module’s backlight typically uses 4 white LEDs in series, drawing about 60-80 mA at 3.3V. If you connect it directly to 5V, you’ll burn out the LEDs, so always use a resistor. The 100-ohm resistor drops 1.7V at 17 mA per LED, which is safe. For brightness control, use a PWM pin like pin 5 on the Uno, with a 100-ohm resistor in series, and set the PWM frequency to 490 Hz (default) to avoid flicker. The ILI9341 itself draws about 20-30 mA from the 3.3V regulator on the module, which is usually a AMS1117-3.3 that can handle up to 800 mA. But if you’re powering the module from the Arduino’s 5V pin, the regulator on the module will dissipate heat: at 80 mA, it’s only 0.14W, so no heatsink needed. However, if you use a 12V supply, the regulator will get hot, and you’ll need a heatsink or a separate 3.3V regulator. The module’s data sheet specifies an operating voltage range of 4.5V to 5.5V for the VCC pin, and the logic pins are 5V tolerant, so you can connect them directly to the Arduino’s 5V outputs without level shifters. But the SD card slot on the module uses 3.3V logic, so you must use a voltage divider or a level shifter for the SD card’s CS, MOSI, and SCK lines if you’re using the SD card. The MISO line from the SD card is open-drain, so it’s safe to connect directly to the Arduino’s 5V pin.

Now, let’s look at the display’s performance metrics. The ILI9341’s pixel clock is 6.5 MHz for 16-bit color, meaning a full-screen fill takes about 240x320x16 bits / 6.5 MHz = 18.9 ms, which is about 53 frames per second. But the Arduino’s SPI at 4 MHz reduces this to 240x320x16 / 4 MHz = 30.7 ms, or 32.5 fps. For static images, this is fine, but for animations, you’ll see tearing if you don’t use double buffering. The Adafruit library supports a tft.fillScreen() function that takes 20 ms, but for complex graphics, you can use the tft.writeRect() function to draw a buffer from RAM. The Arduino Uno has only 2 KB of SRAM, so you can’t store a full frame buffer (240x320x2 bytes = 153.6 KB). Instead, use a 16x16 pixel tile buffer (512 bytes) or a 320x8 pixel row buffer (5.1 KB). The library’s tft.drawRGBBitmap() function can draw from PROGMEM, so you can store images in flash memory. A 240x320 16-bit image takes 153.6 KB, which fits in a 256 KB flash on the Uno, but you’ll have limited space for code. For larger projects, use an Arduino Mega with 8 KB SRAM and 256 KB flash, or a Teensy 3.2 with 64 KB SRAM and 256 KB flash.

Let’s get into the touch screen calibration. The XPT2046 outputs 12-bit values for X and Y, ranging from 0 to 4095. The display’s active area is 240x320 pixels, so you need to map the touch coordinates to pixel coordinates. A typical calibration method: read the touch values at the four corners of the display (e.g., top-left, top-right, bottom-left, bottom-right) and store them in arrays. Then, calculate the mapping factors: scaleX = (touchX_max - touchX_min) / 240 and scaleY = (touchY_max - touchY_min) / 320. Then, for any touch, compute pixelX = (touchX - touchX_min) / scaleX and pixelY = (touchY - touchY_min) / scaleY. This linear mapping works well if the touch screen is aligned, but you might need to swap axes or invert values depending on the module’s orientation. The XPT2046 library by Paul Stoffregen includes a calibration example that saves the coefficients to EEPROM. The touch screen’s response time is about 10 ms, so you can poll it at 100 Hz without issues. But note that the touch screen shares the SPI bus with the display, so you must disable the display’s CS before reading the touch screen, and vice versa. The library handles this automatically if you use the same SPI object.

Now, let’s discuss the SD card slot. The module’s SD card slot uses a separate SPI CS pin (usually pin 4 on the module). The SD card operates at 3.3V logic, so you need a level shifter for the MOSI, MISO, and SCK lines if you’re using the Arduino’s 5V outputs. A simple voltage divider using 10k and 20k resistors works: connect the Arduino’s 5V output to a 10k resistor, then to the SD card’s input, and a 20k resistor to ground. This gives 3.33V at the SD card’s pin. For the MISO line, the SD card outputs 3.3V, which is safe for the Arduino’s 5V input. The SD card’s CS pin also needs to be level-shifted. The SD card library (SdFat or SD) uses the same SPI pins, so you must initialize the SD card after the display, or use a separate SPI object. For the Arduino Uno, you can use software SPI for the SD card by defining pins 4, 11, 12, 13 as CS, MOSI, MISO, SCK, but this is slower. Hardware SPI is faster but requires the level shifter. The SD card’s read speed is about 1-2 MB/s with a class 4 card, which is enough for loading images. You can store 24-bit BMP images on the SD card and display them using the tft.drawBMP() function from the Adafruit library, but the BMP must be 16-bit or 24-bit color, and the resolution must match the display. The library reads the BMP header and converts it to 16-bit RGB565 format. A 240x320 24-bit BMP is 230.4 KB, which takes about 200 ms to load from the SD card at 1 MB/s.

Let’s go deeper into the SPI timing. The ILI9341’s data sheet specifies a minimum SCK high time of 50 ns and low time of 50 ns, so the maximum clock frequency is 10 MHz. The Arduino’s SPI at 4 MHz (250 ns period) is well within spec. But if you use a Teensy or ESP32, you can run at 20 MHz, which reduces the fill time to 240x320x16 / 20 MHz = 6.1 ms, or 163 fps. However, the display’s internal refresh rate is 60 Hz, so you won’t see any benefit beyond 60 fps. The SPI bus also has a propagation delay of about 10 ns per meter, so keep the wires under 30 cm to avoid reflections. Use twisted pair wires for the SPI lines, and add a 100-ohm series resistor at the source to dampen ringing. The module’s PCB traces are 50 ohms, so matching the impedance is not critical for short distances. The display’s backlight PWM frequency should be above 200 Hz to avoid flicker; the Arduino’s default 490 Hz is fine. If you use a higher PWM frequency (e.g., 1000 Hz), you’ll need to change the timer prescaler, which affects other pins.

Now, let’s talk about the physical connections. The module’s pin header is 2.54 mm pitch, and you can use Dupont wires or a ribbon cable. For a permanent setup, solder the wires to the module’s pins. The module’s dimensions are 2.8 inches diagonal, which is about 50.5 mm wide and 67.5 mm tall, with a 0.5 mm thick PCB. The mounting holes are 2.5 mm in diameter, spaced 48 mm apart horizontally and 65 mm vertically. You can mount it on a breadboard or a custom PCB. The module’s backlight LED is on the bottom edge, and the touch screen’s ribbon cable is on the top edge. The SD card slot is on the back of the PCB. The module’s weight is about 15 grams, so it’s light enough for a handheld project. The operating temperature range is -20°C to 70°C, so it’s suitable for indoor use. The display’s viewing angle is 60 degrees in all directions, and the contrast ratio is 500:1 typical. The brightness is 300 cd/m² with the backlight at full power, which is readable in bright indoor light but not in direct sunlight. For outdoor use, you’ll need a polarizer or a sun shield.

Let’s look at the code structure. The Adafruit ILI9341 library includes examples like graphicstest and spitftbitmap. The graphicstest example draws lines, circles, and text, and it’s a good starting point. You need to modify the pin definitions in the sketch: #define TFT_CS 10, #define TFT_DC 9, #define TFT_RST 8. Then, in the setup() function, call tft.begin() and tft.setRotation(1) to set the orientation. The rotation values: 0 is portrait, 1 is landscape, 2 is portrait upside down, 3 is landscape upside down. The library’s tft.drawPixel() function takes 2 microseconds, so drawing a 240x320 bitmap takes 153 ms. For faster drawing, use tft.drawRGBBitmap() with a buffer in PROGMEM. The tft.setAddrWindow() function allows you to draw a rectangular region, which is useful for partial updates. The touch screen library’s example touchtest reads the touch coordinates and prints them to the serial monitor. You can combine both libraries by using the same SPI object, but you must call tft.endSPI() before reading the touch screen, and tft.beginSPI() after. The Adafruit library’s tft.spiBegin() and tft.spiEnd() functions handle this, but they are private. Instead, you can use the SPI.endTransaction() and SPI.beginTransaction() functions with a shared SPI settings object.

Now, let’s discuss common issues and troubleshooting. The most common problem is a blank screen: check the backlight resistor, the VCC voltage (should be 5V), and the SPI wiring. Use a multimeter to measure the voltage at the module’s VCC pin: it should be 5V. If it’s lower, the Arduino’s 5V regulator might be overloaded. Next, check the RESET pin: it should be high (5V) after the boot. If it’s low, the module is in reset. The CS pin must be low to select the display. The DC pin toggles between command and data modes. The SPI clock should be visible on an oscilloscope: a 4 MHz square wave. If you see no clock, the library is not initialized correctly. Another issue is garbled text: this is usually due to wrong pin definitions or a loose connection. The ILI9341 library expects the pins to be defined in the constructor, so double-check the order: CS, DC, RST. If you’re using a different module with a different driver (e.g., ST7789), the library won’t work. The DisplayModule’s DM-TFT28-105 uses the ILI9341, so the Adafruit library is compatible. The touch screen might not respond: check the T_IRQ pin; it goes low when touched. The XPT2046 library’s touch.read() function returns false if no touch is detected. The calibration might be off: try the touch.calibrate() function from the library, which reads the touch values at the corners and stores them in EEPROM. The SD card might not be detected: check the level shifter and the CS pin. The SD card library’s SD.begin() function returns false if the card is not formatted as FAT16 or FAT32. Format the card with a 32 KB cluster size for best performance.

Let’s talk about advanced features. You can use the display’s sleep mode to save power: call tft.sendCommand(ILI9341_SLEEPIN) to put the display to sleep, drawing 5 µA. Wake it up with tft.sendCommand(ILI9341_SLEEPOUT) and wait 120 ms. The backlight can be dimmed with PWM: use analogWrite(backlightPin, 128) for 50% brightness. The display’s gamma correction can be adjusted with the tft.sendCommand(ILI9341_GAMMASET) command

// Next step

See FGCV running against your model, your data, your latency budget.

Get a Production Demo
Back to all articles