How to use a 2.8 inch capacitive TFT display module with a joystick?
How to use a 2.8 inch capacitive TFT display module with a joystick
To use a 2.8 inch capacitive TFT display module with a joystick, you connect the display via SPI or I2C to a microcontroller like an ESP32 or STM32, then wire the joystick’s analog X and Y outputs to ADC pins and its digital switch to a GPIO pin. The display typically runs on the ILI9341 driver at 240x320 resolution, with a capacitive touch controller like the FT6206 or CST816S handling touch input. The joystick adds physical navigation, letting you move a cursor or select items without touching the screen. For example, on an ESP32, you’d use the TFT_eSPI library for the display and analogRead() for the joystick, mapping the joystick’s 0-4095 ADC range to screen coordinates. This setup is common in handheld gaming devices, menu systems, or industrial control panels where tactile feedback is critical. The 2.8 inch capacitive tft display module from DisplayModule supports both SPI and I2C, with a 16-bit parallel interface option for faster refresh rates up to 60 fps. You’ll need to handle the joystick’s mechanical dead zone—typically around 10% of the ADC range—to avoid jitter. Power requirements are straightforward: the display draws about 50 mA at 3.3V, while the joystick adds negligible current, so a 500 mA regulator is plenty for most builds.
Let’s break down the hardware connections first. The display module has 10 pins: VCC, GND, CS, RESET, DC, MOSI, MISO, SCK, LED, and T_IRQ for capacitive touch. For SPI, connect CS to a GPIO (e.g., GPIO5 on ESP32), RESET to GPIO4, DC to GPIO2, MOSI to GPIO23, MISO to GPIO19, SCK to GPIO18, and LED to a 3.3V source through a 100-ohm resistor. The capacitive touch uses I2C: connect T_IRQ to GPIO27, and the touch controller’s SDA and SCL pins to GPIO21 and GPIO22. The joystick has five pins: VCC, GND, VRX (X-axis), VRY (Y-axis), and SW (digital button). Wire VRX to an ADC pin like GPIO34, VRY to GPIO35, and SW to GPIO32 with a 10k pull-up resistor to 3.3V. The joystick’s VCC and GND go to the same 3.3V rail as the display. A common mistake is using 5V for the joystick—most modules are 3.3V tolerant, but check your specific model. The display’s backlight LED pin can be PWM-controlled for brightness, but many just tie it to VCC for full brightness.
Software setup is where the real work happens. For the display, you need the TFT_eSPI library, which handles the ILI9341 driver. Configure the User_Setup.h file with your pin mappings. For the ESP32, a typical setup looks like this:
#define TFT_CS 5
#define TFT_RST 4
#define TFT_DC 2
#define TFT_MOSI 23
#define TFT_MISO 19
#define TFT_SCLK 18
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
For the capacitive touch, use the FT62XX library if your module has the FT6206 controller. Initialize it with Wire.begin(21, 22) for SDA and SCL. The touch controller reports up to 2 simultaneous touches, with coordinates in a 0-240 X and 0-320 Y range. The joystick uses the ESP32’s ADC, which has a 12-bit resolution (0-4095). Read the X and Y values with analogRead(34) and analogRead(35). The joystick’s center position is around 2048, but it varies by module—measure it with a serial monitor during calibration. The dead zone is typically ±200 units around center. The digital switch is active-low, so digitalRead(32) returns LOW when pressed.
Now, let’s talk about integrating the joystick with the display. A practical use case is a menu system where the joystick moves a cursor and the button selects an item. Here’s a high-level algorithm:
1. Read joystick X and Y, map them to display coordinates. For example, map X from 0-4095 to 0-240, and Y from 0-4095 to 0-320. But this is raw—you need to add a dead zone. If the joystick is near center, don’t move the cursor. If it’s outside the dead zone, apply a scaling factor. A common approach is to use a threshold: if abs(X - centerX) > 200, then cursorX += (X - centerX) / 100. This gives smooth movement without jumping.
2. Draw the cursor on the display. Use TFT_eSPI’s fillCircle() or drawPixel() functions. For a 2.8 inch display, a 4-pixel radius cursor is visible. Clear the old cursor position before drawing the new one to avoid ghosting.
3. Handle the joystick button. When digitalRead(32) goes LOW, trigger an action like selecting a menu item. Debounce the button with a 50 ms delay or a timer to avoid multiple triggers.
4. Optionally, use the capacitive touch for tap-to-select. The touch controller reports a touch point when T_IRQ goes LOW. Read the touch coordinates and compare them to menu item rectangles. This gives dual input: joystick for navigation and touch for direct selection.
Performance matters. The ILI9341 at 40 MHz SPI can update a full 240x320 frame in about 26 ms, but partial updates are faster. For a cursor, you only update a small region, so the refresh rate is limited by your loop speed. The joystick reads take about 100 µs per ADC sample, so a loop with two reads, touch check, and display update runs at around 1-2 kHz. That’s overkill for a cursor—limit it to 60 Hz to avoid flicker. Use a millis() timer to control the update rate.
Calibration is a critical step. The joystick’s center point drifts with temperature and age. Store calibration data in EEPROM or NVS. Here’s a calibration routine:
1. Prompt the user to leave the joystick at center for 2 seconds. Read 100 samples and average them for centerX and centerY.
2. Move the joystick to the maximum X position, then minimum, to get rangeX and rangeY. The range is typically 0-4095, but some modules have a narrower swing.
3. Store these values. During operation, map joystick input to screen coordinates using: mappedX = (rawX - centerX) * (240 / rangeX) + 120. This centers the cursor on the screen.
For the capacitive touch, calibration is usually done by the controller’s firmware, but you can add a linear correction if the touch points are offset. The FT6206 has a built-in calibration routine that runs on power-up, but you can also adjust the touch coordinates in software by comparing to known test points.
Power consumption is a consideration for battery-powered projects. The display at full brightness draws 50-60 mA, the touch controller adds 10 mA, and the joystick is passive. Total is about 70 mA at 3.3V. To save power, put the ESP32 into deep sleep and wake it with the joystick button. The display’s sleep mode reduces current to 0.5 mA. Use the displaySleep() function in TFT_eSPI to enter low-power mode. The joystick button can be configured as a wake-up source on the ESP32 using gpio_wakeup_enable().
Let’s look at some real-world data. The 2.8 inch display has a 240x320 resolution with a 16-bit color depth, meaning 153,600 pixels each storing 2 bytes. That’s 307,200 bytes for a full frame buffer. If you’re using an ESP32 with 520 KB SRAM, that’s a tight fit—you might need to use a framebuffer in PSRAM if available. The ILI9341 supports partial display updates, so you can avoid a full buffer. For a cursor, just update a 20x20 pixel area around the cursor position. This reduces memory usage to 800 bytes per update.
The joystick’s mechanical characteristics vary by manufacturer. A typical analog joystick has a 10k-ohm potentiometer for each axis, with a linearity error of ±5%. The center voltage is 1.65V at 3.3V supply, but the ADC reading can fluctuate by ±20 units due to noise. Average multiple readings (e.g., 4 samples) to reduce noise. The joystick’s response time is about 5 ms, so it’s fast enough for real-time control.
For the capacitive touch, the FT6206 supports 2-point touch with a report rate of 100 Hz. The touch resolution is 240x320, matching the display. The touch sensitivity is adjustable via the threshold register (default 0x20 for 32 counts). If you’re getting false touches, increase the threshold. The touch controller communicates over I2C at 400 kHz, so reading touch data takes about 50 µs.
Now, let’s discuss a specific project: a handheld game controller. You’d use the display for graphics, the joystick for movement, and the capacitive touch for menu navigation. The ESP32 can run a game loop at 30 fps, updating the display with sprites. The joystick reads the direction and speed, and the touch detects button presses. For example, a simple maze game: the joystick moves the player, and the touch screen shows a map. The display’s 240x320 resolution is enough for a 20x20 grid of 12x12 pixel cells. The joystick’s analog input gives smooth movement, while the capacitive touch provides a secondary input for inventory or pause.
Another use case is an industrial control panel. The joystick navigates through a list of parameters, and the touch screen selects values. The display’s high contrast (400:1 typical) and wide viewing angle (80 degrees) make it readable in bright light. The capacitive touch works with gloved hands if you adjust the sensitivity. The joystick provides tactile feedback for operators who need to confirm selections without looking at the screen.
Debugging tips: If the display doesn’t work, check the SPI wiring and the TFT_eSPI User_Setup.h file. Common issues include wrong CS or DC pins. For the joystick, if the ADC readings are stuck at 0 or 4095, check the voltage reference—the ESP32’s ADC is sensitive to noise, so add a 100 nF capacitor between VREF and GND. For the capacitive touch, if no touch is detected, ensure the T_IRQ pin is pulled up with a 10k resistor and that the I2C address is correct (0x38 for FT6206, 0x15 for CST816S).
Data table for quick reference:
Display: 2.8 inch, 240x320, ILI9341, SPI up to 40 MHz, 16-bit color, 50 mA at 3.3V
Touch: Capacitive, FT6206 or CST816S, I2C at 400 kHz, 2-point touch, 10 mA
Joystick: Analog, 10k pot, 0-3.3V, 12-bit ADC, 5 ms response, 0 mA (passive)
Microcontroller: ESP32, 240 MHz, 520 KB SRAM, 2x 12-bit ADC, 16x GPIO
Pin mapping example:
Display CS -> GPIO5
Display RESET -> GPIO4
Display DC -> GPIO2
Display MOSI -> GPIO23
Display MISO -> GPIO19
Display SCK -> GPIO18
Touch SDA -> GPIO21
Touch SCL -> GPIO22
Touch IRQ -> GPIO27
Joystick X -> GPIO34 (ADC1_CH6)
Joystick Y -> GPIO35 (ADC1_CH7)
Joystick SW -> GPIO32
Code snippet for reading joystick and touch:
#include
#include
#include
TFT_eSPI tft = TFT_eSPI();
FT62XX ts = FT62XX();
void setup() {
Serial.begin(115200);
tft.begin();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
Wire.begin(21, 22);
ts.begin();
pinMode(32, INPUT_PULLUP);
}
void loop() {
int xJoy = analogRead(34);
int yJoy = analogRead(35);
int btn = digitalRead(32);
if (ts.touched()) {
TS_Point p = ts.getPoint();
tft.fillCircle(p.x, p.y, 4, TFT_RED);
}
if (btn == LOW) {
tft.fillScreen(TFT_BLUE);
}
delay(10);
}
This code reads the joystick and touch, but it’s not optimized. For production, add dead zone handling and debouncing. The FT62XX library might need calibration data—check the library’s documentation for touch coordinate adjustment.
One more thing: the display module’s capacitive touch layer has a glass thickness of about 0.5 mm, which affects touch sensitivity. If you’re using a thick overlay, increase the touch threshold. The joystick’s mechanical life is rated at 500,000 cycles, so it’s durable for most applications. The display’s operating temperature range is -20 to 70°C, making it suitable for outdoor use.
For advanced users, you can implement a gesture recognition system using the joystick. For example, a quick flick in one direction triggers a shortcut. Read the joystick’s velocity by differentiating the position over time. If the velocity exceeds a threshold, execute a gesture. This adds a layer of interaction without using the touch screen.
The capacitive touch can also be used for swipe detection. The FT6206 reports touch coordinates continuously, so you can track the movement vector. If the touch moves more than 50 pixels in one direction within 200 ms, it’s a swipe. Combine this with joystick input for complex interactions, like dragging a slider with the touch while using the joystick to select a value.
In terms of reliability, the display module’s connector is a 0.5 mm pitch FPC, which can be fragile. Use a stiffener or solder wires directly to the breakout board. The joystick’s solder joints are critical—use flux and a temperature-controlled iron at 350°C. The ESP32’s GPIOs are 3.3V tolerant, but the joystick’s output is also 3.3V, so no level shifting is needed.
Finally, testing is essential. Write a diagnostic sketch that shows the joystick’s raw values on the display. Move the joystick to all extremes and verify the cursor moves correctly. Test the touch by drawing a grid of touch points and checking for accuracy. The display’s pixel response time is 20 ms, so there’s no noticeable lag. The joystick’s analog output has a 10 ms settling time, so sample after a delay if you’re reading it in a fast loop.