How to use a 2.4 inch resistive TFT display with STM32?
How to use a 2.4 inch resistive TFT display with STM32
To use a 2.4 inch resistive TFT display with an STM32 microcontroller, you need to connect the display’s parallel or SPI interface to the STM32’s GPIO pins, initialize the display controller (typically ILI9341 or ST7789V), and then write pixel data to the frame buffer. The most common approach is using the SPI protocol because it requires fewer pins—usually 4 to 6 lines—and is supported by almost all STM32 models. For example, the STM32F103C8T6 (Blue Pill) can drive a 240x320 resolution display at 18-bit color depth using SPI clock speeds up to 36 MHz, achieving refresh rates around 30-60 Hz depending on the data transfer overhead. The resistive touch layer adds two analog inputs (X+ and X- for the X-axis, Y+ and Y- for the Y-axis) that you read via an ADC or a dedicated touch controller like the XPT2046. If you’re using a 2.4 inch resistive tft display module, it often integrates the touch controller, simplifying the wiring. The key is to match the display’s logic voltage (usually 3.3V) with the STM32’s I/O levels, and to handle the resistive touch debouncing in software. Below, I’ll break down the hardware connections, initialization sequence, touch reading, and performance optimization with specific data and code examples.
Hardware Connections and Pin Mapping
Most 2.4-inch resistive TFT displays use a 16-pin or 14-pin header with SPI interface. The pinout typically includes: VCC (3.3V), GND, CS (chip select), RESET, DC (data/command), MOSI, SCK, LED (backlight), and the touch pins (T_IRQ, T_DO, T_DIN, T_CS, T_CLK). For the STM32, connect VCC to 3.3V, GND to ground, and the SPI lines to the STM32’s SPI peripheral pins. For example, on an STM32F103, SPI1 uses PA5 (SCK), PA6 (MISO), PA7 (MOSI), and you can assign CS to PA4, DC to PA3, RESET to PA2, and LED to PA1 with a PWM-capable timer for brightness control. The resistive touch controller (XPT2046) uses a separate SPI bus or shares the same bus with different CS pins—many modules share the SPI lines but use separate CS for the display and touch. The touch pins: T_IRQ connects to a GPIO interrupt pin (e.g., PB0), T_CS to PB1, T_DIN to PA7 (shared MOSI), T_DO to PA6 (shared MISO), and T_CLK to PA5 (shared SCK). This shared setup works because the display and touch are never accessed simultaneously. The backlight LED typically draws 20-30 mA at 3.3V, so you can drive it directly from a GPIO pin or use a transistor if you need PWM dimming. For a 2.4 inch resistive tft display, the resistive touch layer requires two analog inputs: you can read the X and Y positions by setting the touch controller to differential mode and measuring the voltage on the X+ and Y+ pins. The XPT2046 handles this internally, outputting 12-bit digital values (0-4095) that map to the 240x320 pixel grid. The table below shows a typical wiring for STM32F103:
| Display Pin | STM32 Pin | Function |
|---|---|---|
| VCC | 3.3V | Power |
| GND | GND | Ground |
| CS | PA4 | SPI chip select |
| RESET | PA2 | Reset |
| DC | PA3 | Data/command |
| MOSI | PA7 | SPI master out |
| SCK | PA5 | SPI clock |
| LED | PA1 | Backlight (PWM) |
| T_IRQ | PB0 | Touch interrupt |
| T_CS | PB1 | Touch chip select |
| T_DIN | PA7 | Touch SPI MOSI |
| T_DO | PA6 | Touch SPI MISO |
| T_CLK | PA5 | Touch SPI SCK |
Initializing the Display Controller
The display controller (ST7789V or ILI9341) requires a specific initialization sequence after power-up. For ST7789V, which is common in 2.4-inch modules, the sequence includes: hardware reset (hold RESET low for 10ms, then high), software reset (command 0x01), sleep out (0x11), and then set the color mode (0x3A) to 0x05 for 16-bit RGB565 or 0x06 for 18-bit. Then you set the memory access control (0x36) to define the scan direction—typically 0x00 for landscape or 0xA0 for portrait. The display’s column and page address are set via commands 0x2A and 0x2B, with start and end coordinates (0,0 to 239,319 for 240x320). Finally, send command 0x29 to turn on the display. The entire initialization takes about 120-150ms, and you must wait for the display’s internal oscillator to stabilize. For the ILI9341, the sequence is similar but includes additional commands like 0xCF for power control and 0xC0 for VCOM control. The exact initialization bytes depend on the module manufacturer; many Chinese modules use a 16-bit parallel interface but are configured for SPI by default. If you’re using a 2.4 inch resistive tft display, the manufacturer provides a datasheet with the initialization code. Here’s a typical initialization function in C for STM32 using HAL library:
Code Snippet: Display Initialization
void TFT_Init(void) {
HAL_GPIO_WritePin(TFT_CS_GPIO_Port, TFT_CS_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TFT_RESET_GPIO_Port, TFT_RESET_Pin, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(TFT_RESET_GPIO_Port, TFT_RESET_Pin, GPIO_PIN_SET);
HAL_Delay(120);
TFT_WriteCmd(0x01); // Software reset
HAL_Delay(150);
TFT_WriteCmd(0x11); // Sleep out
HAL_Delay(200);
TFT_WriteCmd(0x3A); // Color mode
TFT_WriteData(0x05); // 16-bit RGB565
TFT_WriteCmd(0x36); // Memory access control
TFT_WriteData(0x00); // Landscape orientation
TFT_WriteCmd(0x2A); // Column address set
TFT_WriteData(0x00); TFT_WriteData(0x00); // Start column
TFT_WriteData(0x00); TFT_WriteData(0xEF); // End column (239)
TFT_WriteCmd(0x2B); // Page address set
TFT_WriteData(0x00); TFT_WriteData(0x00); // Start page
TFT_WriteData(0x01); TFT_WriteData(0x3F); // End page (319)
TFT_WriteCmd(0x29); // Display on
HAL_Delay(50);
TFT_FillScreen(0x0000); // Clear to black
}
Writing Pixel Data and Frame Buffer Management
Once initialized, you write pixels by setting the window (column and page addresses) and then sending pixel data. For 16-bit color, each pixel is two bytes (RGB565 format: 5 bits red, 6 bits green, 5 bits blue). The STM32’s SPI peripheral can send data in blocks of up to 65535 bytes, but for a full 240x320 frame (153,600 pixels), you need to send 307,200 bytes. At 36 MHz SPI clock, a single byte takes about 0.28 µs, so the theoretical transfer time for one frame is 307,200 * 0.28 µs = 86 ms, which gives about 11.6 FPS. However, overhead from command frames and GPIO toggling reduces this to around 8-10 FPS. To improve performance, you can use DMA (Direct Memory Access) to send pixel data without CPU intervention. For example, configure SPI1 with DMA1 channel 3 for TX, and set up a buffer of 307,200 bytes in SRAM. The STM32F103 has 64KB SRAM, which is not enough for a full frame buffer (307KB), so you need to use a smaller buffer and send in chunks. A common technique is to use a line buffer of 480 bytes (240 pixels * 2 bytes) and send each line sequentially. This reduces SRAM usage to 480 bytes and allows the CPU to prepare the next line while DMA sends the current one. The STM32F4 series has more SRAM (192KB) and can store a full frame buffer, but for 2.4-inch displays, line-by-line rendering is sufficient for static images. For dynamic content like animations, you can use double buffering with two line buffers to avoid tearing. The display’s write speed also depends on the SPI clock divider; set the SPI prescaler to 2 (36 MHz) for STM32F103 at 72 MHz system clock, or to 4 (18 MHz) if signal integrity is poor due to long wires.
Reading Resistive Touch Input
The resistive touch layer uses the XPT2046 controller, which communicates via SPI and outputs 12-bit data for X and Y positions. The touch panel has four wires: X+, X-, Y+, Y- (or integrated into the module’s pins). The XPT2046 reads the touch position by applying a voltage across one axis and measuring the voltage on the other axis. The typical sequence: set the touch CS low, send a control byte (0x90 for X measurement, 0xD0 for Y measurement), then read two bytes (the first byte contains the high 8 bits, the second byte contains the low 4 bits in the high nibble, so you shift and combine). The raw values range from 0 to 4095, but they need to be calibrated to the display’s pixel coordinates. For a 240x320 display, the X range might be 200-3800 and Y range 150-3900, depending on the touch panel’s alignment. You can calibrate by touching known corners and mapping the values linearly. For example, if the top-left corner gives (Xmin, Ymin) and bottom-right gives (Xmax, Ymax), then pixel X = (rawX - Xmin) * 240 / (Xmax - Xmin). The touch interrupt pin (T_IRQ) goes low when a touch is detected, so you can configure an external interrupt on PB0 to trigger a reading. Debouncing is critical because resistive touch can produce noise: sample the touch position 10 times with 1ms intervals and average the values, or use a median filter. The XPT2046 also supports pressure measurement by reading the touch resistance (command 0xB0), but this is rarely used in simple applications. Here’s an example function to read X and Y:
Code Snippet: Touch Read
uint16_t Touch_ReadX(void) {
uint8_t buf[2];
HAL_GPIO_WritePin(T_CS_GPIO_Port, T_CS_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t*)0x90, 1, 10);
HAL_SPI_Receive(&hspi1, buf, 2, 10);
HAL_GPIO_WritePin(T_CS_GPIO_Port, T_CS_Pin, GPIO_PIN_SET);
return ((buf[0] << 4) | (buf[1] >> 4));
}
Performance Optimization and Real-World Data
Using a 2.4-inch resistive TFT display with STM32 involves trade-offs between resolution, color depth, and refresh rate. At 240x320 resolution with 16-bit color, the SPI bus is the bottleneck. The STM32F103 can achieve 8-10 FPS with DMA and line buffers, while the STM32F407 can reach 20-25 FPS due to faster SPI (up to 42 MHz) and larger SRAM for full frame buffering. The resistive touch adds latency: reading X and Y takes about 2 ms per sample (including SPI transactions and averaging), so you can poll at 100 Hz without affecting display performance. Power consumption is also a factor: the display backlight draws 20-30 mA, the STM32F103 draws about 50 mA at 72 MHz, and the touch controller draws 1 mA, totaling around 80-100 mA. For battery-powered projects, you can reduce the backlight PWM to 50% duty cycle, which cuts current to 15 mA while maintaining readability. The display’s response time is about 20 ms (typical for TFT), so fast-moving objects may show motion blur. If you need higher frame rates, consider using a parallel interface (8080 or 6800 mode) with 8-bit or 16-bit data lines, but this requires more GPIO pins (16-20 pins) and is only feasible on STM32 packages with 100+ pins. The SPI approach is simpler and works with 8-pin SPI modules. For the resistive touch, the accuracy is about 1% of the full scale (around 2-3 pixels), which is sufficient for button presses but not for precise drawing. You can improve accuracy by using a 4-point calibration and interpolation. The touch panel’s lifespan is typically 1 million touches, so it’s durable for most applications.
Common Pitfalls and Debugging Tips
One common issue is the display not initializing because the SPI clock polarity (CPOL) and phase (CPHA) are set incorrectly. Most TFT controllers use mode 0 (CPOL=0, CPHA=0) or mode 3 (CPOL=1, CPHA=1). Check the datasheet: ST7789V typically uses mode 0, while ILI9341 uses mode 0 as well. If the display shows random colors, the color mode command might be wrong—ensure you send 0x05 for 16-bit, not 0x06. Another issue is the resistive touch returning zero values: this often happens if the touch CS pin is not pulled high after the transaction, or if the SPI lines are shared with the display and the display’s CS is not deselected. Always set the display CS high before accessing the touch. The touch interrupt pin may be active low, but some modules have a pull-up resistor, so you need to configure the STM32’s GPIO as input with internal pull-up. If the touch readings are noisy, add a 100nF capacitor between the touch analog pins and ground. The backlight may flicker if you use a simple GPIO without PWM; use a timer output compare channel (e.g., TIM2_CH1 on PA1) with a 1 kHz frequency and adjust duty cycle via the CCR register. For the STM32F103, the maximum SPI clock is 18 MHz when using the APB2 bus (SPI1), but you can push it to 36 MHz with a 72 MHz system clock and a prescaler of 2, though signal integrity may degrade on breadboards. Use short wires (under 10 cm) and add 10 ohm series resistors on the SPI lines to reduce ringing. If the display still doesn’t work, use a logic analyzer to verify the SPI signals: the CS must go low before the clock, and the DC pin must be set high for data and low for commands. The initialization sequence timing is critical; some modules require a 5ms delay after the software reset command, not 150ms, so check the specific controller’s datasheet. For the resistive touch, the XPT2046 has a 12-bit ADC with a conversion time of 2.5 µs, but the SPI transaction adds overhead. You can reduce the SPI clock for the touch to 1 MHz to avoid noise, as the touch readings don’t need high speed.
Advanced Techniques: Using FreeRTOS and Touch Calibration
For multitasking, you can run the display update in a low-priority task and the touch reading in a high-priority interrupt-driven task. Use FreeRTOS queues to pass touch coordinates from
Next Step