Skip to content
Louisville 2013The Archive · Vol. XI
The 2013 Archive · Volume XI

How to use a 2.4 inch resistive TFT display with a display library?

By admin Louisville 2013

How to use a 2.4 inch resistive TFT display with a display library

To use a 2.4 inch resistive TFT display with a display library, you first need to identify the specific controller chip on the module—most commonly the ST7789V or ILI9341. For a typical 240x320 resolution panel with a resistive touch overlay, the ST7789V is widely used in modules like the 2.4 inch resistive tft display from DisplayModule. You’ll wire it via SPI (Serial Peripheral Interface) to a microcontroller such as an ESP32, STM32, or Raspberry Pi Pico. The library you choose—like Adafruit_ST7789 or TFT_eSPI—handles pixel-level commands, but you must configure pin mappings, SPI speed, and color depth in the library’s user setup file. Resistive touch requires an additional ADC (analog-to-digital converter) like the XPT2046, which is often integrated on the same PCB. You’ll need to read raw X and Y voltages from the touch controller, apply calibration offsets, and map them to the display coordinates. Start by initializing the display with a 4-wire SPI connection: MOSI, SCK, CS, and DC, plus a reset pin. Set the SPI clock to 20 MHz for stable operation on most 3.3V logic boards. After wiring, install the library via Arduino IDE or PlatformIO, then run the built-in test sketch to verify pixel rendering. For touch, you’ll need a separate library like XPT2046_Touchscreen, which reads pressure and position data. Calibrate by touching known corners and storing min/max values in EEPROM. The resistive layer is pressure-sensitive, so you must apply firm but gentle force—unlike capacitive, it works with any stylus or gloved finger. Frame rate with the ST7789V at 240x320 can hit 30-40 FPS using 16-bit color (RGB565) over SPI, but drops if you enable double buffering or software rotation. Use DMA (Direct Memory Access) on supported microcontrollers to push pixel data without blocking the CPU. For example, on an ESP32, you can allocate a 153600-byte buffer (320*240*2) for full-screen redraws, but that eats into RAM. Instead, use partial updates—send only changed regions via setAddrWindow(). Resistive touch accuracy is about ±2% of the panel size after calibration, but drift occurs with temperature changes. You can mitigate this by averaging 5-10 samples per touch event. The display’s typical power consumption is 80-120 mA at 3.3V with backlight on, so plan your power supply accordingly. If you’re using a battery-powered device, disable the backlight via a PWM pin when idle. The viewing angle is limited—around 60 degrees horizontal and 40 degrees vertical—due to the TN (Twisted Nematic) panel technology. Contrast ratio is about 500:1, and response time is 10-15 ms, which is fine for UI menus but not for fast video. Resistive touch has a lifespan of about 1 million touches at a single point, so avoid repeated presses on the same spot. For the library setup, you’ll need to define these constants in TFT_eSPI’s User_Setup.h file: TFT_MISO (if used), TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, TFT_RST, and TOUCH_CS for the touch controller. For the ST7789V, set the display offset to 0,0 and the rotation to 0 for portrait mode. If you see garbled colors, flip the RGB order in the library. The SPI mode should be 0 (CPOL=0, CPHA=0) for most controllers. You can also use 8-bit parallel interface on some boards, but that requires 8+ data lines and limits pin availability. Resistive touch uses a 4-wire analog interface: X+, X-, Y+, Y-. The XPT2046 reads these as 12-bit values (0-4095). To convert to pixel coordinates, use this formula: pixel_x = (raw_x - x_min) * (screen_width - 1) / (x_max - x_min). You’ll need to store the min and max values from a four-corner calibration routine. For a 240x320 display, typical raw values range from 200 to 3800, but this varies with panel manufacturing. The touch pressure is read from the Z-axis channel; a threshold of 200-400 indicates a valid press. If you’re using a library like LVGL (LittlevGL), you can integrate the display and touch drivers via a display driver interface (e.g., disp_flush() and touch_read()). LVGL’s buffer size should be at least 1/10 of the screen area—say 7680 bytes for a 240x320 display with 16-bit color. The resistive touch driver in LVGL returns pressure and coordinates, which you can map to input events. For real-time applications, avoid using software SPI bit-banging; hardware SPI with DMA is 10x faster. On a Raspberry Pi Pico, you can run the display at 62.5 MHz SPI clock if you use the PIO (Programmable I/O) state machine. The ST7789V supports a 16-bit color mode, but you can also use 8-bit mode to save RAM at the cost of color fidelity. The display’s gamma curve can be adjusted via the ST7789V’s internal registers—write to command 0xE0 for positive gamma and 0xE1 for negative gamma. Default gamma values are 0x70, 0x04, 0x08, 0x09, 0x09, 0x05, 0x16, 0x0C, 0x3F, 0x0C, 0x0A, 0x27, 0x2B, 0x0C for positive gamma. You can tweak these to improve contrast for your specific panel. The resistive touch layer has a surface resistance of 200-900 ohms per square, and the sensitivity varies with the stylus tip size—a 2mm tip works best. Avoid using sharp objects that can scratch the polyester film. The display module’s backlight is usually driven by a boost converter; check the datasheet for maximum current (often 20-30 mA per LED string). If you’re using a 3.3V supply, the backlight voltage might be 10-12V, so don’t connect it directly to a GPIO pin. Use a MOSFET or a dedicated backlight driver IC. For the display library, you can also use the Arduino_GFX library, which supports multiple controllers and has built-in touch calibration. It uses a single class for both display and touch, reducing code size. The initialization sequence for the ST7789V involves sending a software reset (0x01), then sleep out (0x11), then display on (0x29). Wait 120 ms after reset before sending commands. The library handles this automatically, but if you’re writing a custom driver, you must follow the timing. The display’s refresh rate is 60 Hz by default, but you can reduce it to 30 Hz to save power. For resistive touch, the XPT2046’s sampling rate is 125 kHz, so you can read touch at 100 Hz without issues. If you’re using a multitouch scenario, note that resistive touch only supports single-touch—it cannot detect multiple simultaneous contacts. The touch controller’s IRQ pin can be used to trigger an interrupt on press, reducing CPU load. In the library, you’ll set the touch interrupt pin to INPUT_PULLUP and read the IRQ state before initiating a SPI transaction. The XPT2046’s command byte is 0x90 for X, 0xD0 for Y, and 0xB0 for Z. You can also read battery voltage from the auxiliary channel (0xE0) if the module has a battery connection. For the display, the ST7789V’s pixel format register (0x3A) should be set to 0x55 for 16-bit color (RGB565). If you see inverted colors, check the bit order—some libraries expect RGB, others BGR. The display’s sleep mode (0x10) reduces current to under 1 mA, but you must wake it with a 120 ms delay. For a production device, you’ll want to store calibration data in non-volatile memory. On an ESP32, use Preferences library; on an STM32, use EEPROM emulation. The calibration values should be floats or integers scaled to 0.1 resolution. A typical calibration routine involves touching four corners and the center, then calculating linear transformation coefficients. The formula for X: screen_x = (raw_x - offset_x) * scale_x, where offset_x is the raw value at the left edge, and scale_x is (screen_width) / (right_raw - left_raw). You can also use a 2D affine transformation for non-linear distortions, but resistive touch is usually linear enough. The display’s polarizer angle is fixed, so viewing from the side will invert colors—this is normal for TN panels. If you need wider viewing angles, consider an IPS version, but resistive touch is rare on IPS. The module’s PCB often has a 14-pin or 16-pin FPC connector with 1.0mm pitch. Typical pinout: 1=VCC, 2=GND, 3=CS, 4=RESET, 5=DC, 6=SDI(MOSI), 7=SCK, 8=LED, 9=SDO(MISO), 10=T_CLK, 11=T_CS, 12=T_DIN, 13=T_DO, 14=T_IRQ. Double-check your module’s datasheet because pin order varies. The backlight pin (LED) may be active-high or active-low—test with a 1k resistor to 3.3V. For the display library, the TFT_eSPI library has a built-in touch calibration function called setTouch() that stores calibration data in a struct. You can call it once during setup and reuse the values. The library also supports rotation for both display and touch; ensure both are rotated consistently. If you’re using a 3D printer or CNC controller, the resistive touch can be used for menu navigation, but avoid using it in dusty environments because the film can trap particles. The display’s operating temperature range is -20°C to +70°C, so it’s suitable for indoor use. For outdoor use, the backlight brightness of 300 cd/m² is barely readable in direct sunlight—you’ll need a sunshade. The resistive touch layer adds about 10% light loss, so the effective brightness is lower. The display’s refresh rate with the library can be optimized by using a 4-bit SPI mode (if the controller supports it) or by using a parallel interface. On a Teensy 4.0, you can achieve 60 FPS with a 240x320 display using the ILI9341 library. The ST7789V is similar but has a slightly different command set—for example, the memory data access control register (0x36) controls rotation. Set it to 0x00 for portrait, 0x60 for landscape, 0xC0 for inverted portrait, 0xA0 for inverted landscape. The touch controller’s SPI mode is 0 as well, but you can share the same SPI bus with the display if you use separate CS pins. The maximum SPI speed for the XPT2046 is 2 MHz, so keep that in mind if you’re sharing the bus with the display at 20 MHz. You can use a separate SPI bus for the touch controller to avoid conflicts. The library’s touch read function should be called in the main loop, not in an interrupt, because SPI transactions can take up to 1 ms. For debouncing, ignore touch events that last less than 50 ms. The display’s pixel memory is organized as a 240x320 array of 16-bit words. Writing to the display requires sending a command byte followed by data bytes. The library handles this with a write command function. If you’re using a custom library, you’ll need to implement the following functions: init(), setAddrWindow(), pushColor(), fillScreen(), drawPixel(), drawLine(), drawRect(), fillRect(), drawCircle(), drawTriangle(), drawChar(), setCursor(), setTextColor(), setTextSize(), and print(). The print function is useful for debugging. For the resistive touch, you’ll need to implement getPoint() that returns X, Y, and pressure. The library’s touch calibration function can be called after a button press to enter calibration mode. The typical calibration screen shows a crosshair at each corner; the user touches it, and the library stores the raw values. The number of calibration points can be 3 or 4, but 4-point calibration is more accurate. The display’s backlight can be controlled with a PWM frequency of 1-5 kHz to avoid audible whine. The duty cycle should be 0-100% but note that at very low duty cycles, the backlight may flicker. Use a 1kHz PWM with 8-bit resolution for smooth dimming. The display’s power consumption at full brightness is about 100 mA, but at 50% brightness, it drops to 60 mA. The resistive touch layer draws negligible current (<1 mA). The total system power for an ESP32 with the display on is around 200 mA, so use a 500 mA regulator. For battery operation, use a deep sleep mode where the display is turned off and the ESP32 wakes up on a touch interrupt. The resistive touch can be used as a wake-up source by connecting the IRQ pin to a GPIO that supports wake from deep sleep. The library’s touch interrupt handler should be minimal—just set a flag. In the main loop, check the flag and read the touch coordinates. If you’re using a display library like u8g2, it supports monochrome displays but not color TFTs. For color TFTs, stick with TFT_eSPI or Adafruit_ST7789. The Adafruit library uses a GFX base class, so you can use the same drawing functions as other Adafruit displays. The library’s RAM usage is about 2-4 KB for the buffer, plus 1-2 KB for the font cache. The ST7789V’s command set includes 0x2A for column address set, 0x2B for row address set, and 0x2C for memory write. The library sends these commands for each pixel update. For fast screen updates, use the 0x2C command with a burst of pixel data. The display’s internal RAM is 172800 bytes (240*320*18 bits for 18-bit color), but the library uses 16-bit color, so it’s 153600 bytes. The display can handle 18-bit color if you set the pixel format register to 0x66, but the library will truncate to 16-bit. The resistive touch’s accuracy can be improved by using a median filter on the raw values. Take 5 samples, sort them, and pick the middle one. This reduces noise from the ADC. The XPT2046’s internal reference voltage is 2.5V, so the raw values correspond to a voltage divider. The touch pressure is inversely proportional to the Z value; a higher Z means lighter touch. The threshold for a valid touch should be set empirically. For a typical panel, a Z value of 200-300 indicates a light touch, while 1000+ indicates a hard press. The library’s touch detection should ignore values below 200 to avoid false triggers. The display’s SPI bus can be shared with other devices if you use separate CS pins and ensure the devices are not driving the MISO line simultaneously. The ST7789V’s MISO pin is optional; many modules don’t connect it, so you can leave it floating. The library will work without MISO if you set TFT_MISO to -1. The touch controller’s MISO is required because it sends data. The display’s reset pin can be connected to the microcontroller’s reset pin, but it’s better to use a separate GPIO so you can reset the display without resetting the MCU. The initialization sequence should be: set reset pin low, wait 10 ms, set high, wait 120 ms, then send commands. The library does this automatically. The display’s sleep mode can be entered by sending command 0x10, then waiting 5 ms. To wake, send 0x11, wait 120 ms, then 0x29. The library’s displayPowerSave() function handles this. The resistive touch’s IRQ pin is active low; it goes low when the screen is pressed. You can use this to trigger an interrupt that sets a flag. In the interrupt handler, don’t do SPI transactions—just set a volatile boolean. In the main loop, check the flag and read the touch if the flag is set. The flag should be cleared after reading. The touch read function should take less than 1 ms, so it won’t block the main loop. The display’s frame rate can be measured by toggling a GPIO at the start and end of the flush function. The library’s flush function is called by LVGL or other GUI frameworks. The typical flush time for a 240x320 display is 5-10 ms at 20 MHz SPI. With DMA, it can be 2-3 ms. The total frame time with touch processing is 10-15 ms, so you can achieve 60 FPS. The display’s pixel clock is 20 MHz, so it takes 240*320*2 bytes / 20 MHz = 7.68 ms to send a full frame. The library’s overhead adds another 2-3 ms. The resistive touch’s sampling rate is 125 kHz, so you can read touch at 100 Hz without issues. The touch controller’s conversion time is 2.5 us per channel, so reading X, Y, and Z takes 7.5 us plus SPI overhead. The total touch read time is about 50 us at 2 MHz SPI. The library’s touch read function should be called at least 20 times per second for smooth interaction. The display’s color depth is 16-bit (65,536 colors), which is sufficient for most UI elements. The library’s color macros like ST77XX_BLACK (0x0000) and ST77XX_WHITE (0xFFFF) are

End of filing
The Quarterly Briefing

Get the 2013 story, retold four times a year.

Primary documents, oral histories and original reporting from the Louisville 2013 archive — delivered to 41,800 readers across 38 states and 12 countries.

Subscribe to the 2013 Archive