Skip to content

How to use a 2.4 inch 240x320 TFT display with a camera module?

To use a 2.4 inch 240x320 TFT display with a camera module, you need to connect the display’s parallel or SPI interface to a microcontroller that can also handle the camera’s data stream, typically via a dedicated camera interface like DCMI on STM32 or using GPIO bit-banging on slower MCUs. The display itself, such as the 2.4 inch 240x320 tft display, usually operates with an ILI9341 or ST7789 driver, which supports both 8-bit parallel and 4-wire SPI modes. For real-time camera preview, you’ll need a microcontroller with at least 512KB of RAM to buffer a 240x320 frame at 16-bit color depth (153,600 bytes per frame), and a clock speed above 72 MHz to handle the display refresh and camera capture simultaneously. The camera module, like an OV2640 or OV7670, outputs JPEG or raw RGB data via a 8-bit parallel bus, which requires precise timing—typically 24 MHz pixel clock for VGA resolution. To get a working setup, you’ll wire the display’s CS, DC, SCK, MOSI, and MISO pins to the MCU’s SPI pins, and the camera’s VSYNC, HSYNC, PCLK, and D0-D7 to the MCU’s GPIO or DCMI pins. Power is critical: the display draws about 80-120 mA at 3.3V with backlight on, while the camera module pulls 50-70 mA, so use a 3.3V regulator with at least 500 mA capacity. Let’s break down the hardware, software, and performance considerations with hard data.

Hardware Interface and Pin Mapping

The 2.4 inch 240x320 tft display typically uses the ILI9341 controller, which supports SPI mode up to 40 MHz and 8-bit parallel mode up to 10 MHz. For camera integration, SPI is simpler but slower—you’ll get around 15-20 frames per second (FPS) at 240x320 resolution if you optimize the SPI clock to 40 MHz and use DMA. Parallel mode can push 30-40 FPS but requires 8 data lines plus control pins. Here’s a typical pin mapping for an STM32F407 MCU:

Display Pin Function MCU Pin (STM32F407) Notes
VCC 3.3V Power 3.3V rail 100-120 mA draw
GND Ground GND Common ground
CS Chip Select PB0 Active low
DC Data/Command PB1 High = data, Low = command
SCK SPI Clock PA5 (SPI1_SCK) Up to 40 MHz
MOSI SPI Data PA7 (SPI1_MOSI) Display input only
MISO SPI Data (optional) PA6 (SPI1_MISO) Not used for write-only
LED Backlight PB2 (PWM) PWM for brightness

For the camera module, say an OV2640, you’ll need these connections:

Camera Pin Function MCU Pin (STM32F407) Notes
VCC 3.3V Power 3.3V rail 50-70 mA draw
GND Ground GND Common ground
VSYNC Vertical Sync PA8 (DCMI_VSYNC) Falling edge indicates frame start
HSYNC Horizontal Sync PA9 (DCMI_HSYNC) Active low during line
PCLK Pixel Clock PA10 (DCMI_PCLK) 24 MHz typical
D0-D7 Data Bus PC0-PC7 (DCMI_D0-D7) 8-bit parallel
SDA I2C Data PB7 (I2C1_SDA) For camera configuration
SCL I2C Clock PB6 (I2C1_SCL) 100-400 kHz

You’ll also need a 3.3V regulator like the AMS1117-3.3, which can handle 1A peak, plus decoupling capacitors (10 µF and 0.1 µF) near both modules to filter noise. The camera’s I2C lines need 4.7kΩ pull-up resistors to 3.3V. For the display backlight, a PWM pin on the MCU (e.g., TIM3_CH1 on PB2) at 1 kHz frequency with 50% duty cycle gives about 80 cd/m² brightness, which is fine for indoor use. If you use a 5V Arduino Uno, you’ll need level shifters for the SPI lines because the display runs at 3.3V logic—use a 74LVC245 buffer to avoid damage. The camera module’s internal oscillator is 24 MHz, but you can set it to 20 MHz via I2C to reduce noise if your wiring is long.

Software Stack and Frame Buffer Management

On the software side, you’ll need a real-time operating system (RTOS) like FreeRTOS on an STM32 to manage the camera capture and display refresh without dropping frames. The camera outputs data in JPEG or raw RGB565 format. For raw RGB, each pixel is 2 bytes (16-bit), so a full 240x320 frame is 153,600 bytes. If you use double buffering, you need 307,200 bytes of RAM—most STM32F4 series have 192 KB SRAM, which isn’t enough. You can use external SDRAM (e.g., 8 MB via FSMC) or compress to JPEG: the OV2640 can output JPEG at 240x320 with a quality factor of 50, resulting in 10-20 KB per frame, which fits in internal SRAM. Here’s a typical software flow:

1. Camera Initialization: Configure the OV2640 via I2C with registers like 0xFF = 0x01 (reset), then set output format to JPEG (0xDA = 0x10) and resolution to 240x320 (0xE0 = 0x14). Use a 24 MHz pixel clock. The camera’s internal PLL can be set to 48 MHz for the sensor, but the pixel clock divider should be set to 2 for 24 MHz output. Use the HAL_DCMI_Start() function to enable capture with DMA.

2. Display Initialization: Send the ILI9341 initialization sequence via SPI: 0x01 (reset), 0x11 (sleep out), 0x36 (set MADCTL for orientation), 0x3A (pixel format to 0x55 for 16-bit), 0x29 (display on). Set the SPI clock to 40 MHz and use DMA for bulk data transfer. The display’s memory write command (0x2C) sends pixel data sequentially—set the column (0x2A) and page (0x2B) address range to 0,0 to 239,319.

3. Capture Loop: In the RTOS task, wait for a VSYNC interrupt from the camera. When VSYNC goes low, start DMA to capture a line of pixels (240 pixels = 480 bytes for RGB565, or variable for JPEG). For JPEG, the camera sends a header (0xFF 0xD8) and EOF marker (0xFF 0xD9). Use a circular buffer to store the JPEG data, then decode it with a lightweight library like TJpgDec on the MCU, which decompresses to a 240x320 RGB565 buffer in external SDRAM. The decoding time for a 15 KB JPEG is about 30-50 ms on a 168 MHz Cortex-M4, so you can achieve 15-20 FPS. For raw RGB, capture directly to the buffer and send to the display using DMA.

4. Display Refresh: After the buffer is ready, set the display’s address window to the full screen and send the 153,600 bytes via SPI using DMA. At 40 MHz SPI clock, transferring 153,600 bytes takes about 3.8 ms (153600 * 8 / 40e6 = 0.03072 seconds? Let’s recalculate: 153600 bytes * 8 bits/byte = 1,228,800 bits. At 40 MHz, that’s 1,228,800 / 40,000,000 = 0.03072 seconds, or 30.7 ms. But with SPI overhead and DMA setup, expect 35-40 ms. So the theoretical maximum FPS is 1000 / 40 = 25 FPS, but with camera capture and JPEG decode, you’ll get 10-15 FPS in practice.

Here’s a performance comparison for different setups:

Setup MCU Display Interface Camera Format RAM Used FPS Latency (ms)
SPI + JPEG STM32F407 (168 MHz) SPI 40 MHz JPEG (15 KB) 200 KB (internal) 12-15 80-100
Parallel + Raw STM32F429 (180 MHz) 8-bit parallel 10 MHz Raw RGB565 (153 KB) 512 KB (external SDRAM) 25-30 35-50
SPI + Raw (no decode) ESP32 (240 MHz) SPI 20 MHz Raw RGB565 (153 KB) 320 KB (PSRAM) 8-10 100-120

The latency includes camera capture time (about 33 ms for 30 FPS raw), display transfer time, and any processing. For the ESP32, the SPI is limited to 20 MHz due to GPIO timing, and the camera uses the I2S interface for parallel data, which adds overhead. The STM32F429 with parallel display and external SDRAM gives the best performance because the display write speed is higher (10 MHz parallel = 80 Mbps, compared to 40 MHz SPI = 40 Mbps) and the SDRAM allows fast buffer swaps.

Power and Thermal Constraints

Power consumption is a key factor for portable projects. The 2.4 inch 240x320 tft display with backlight at 50% duty draws about 100 mA from 3.3V, or 330 mW. The OV2640 camera module draws 60 mA at 3.3V, or 198 mW. The STM32F407 at 168 MHz draws about 100 mA, or 330 mW. Total system power is around 858 mW, which means a 1000 mAh Li-Po battery at 3.7V (3.7 Wh) would run for about 4.3 hours. If you use the display at full brightness (120 mA), it jumps to 396 mW, reducing runtime to 3.5 hours. The camera’s JPEG mode actually draws less power than raw mode because the internal compression reduces data output, but the sensor core still runs at the same clock. You can reduce power by lowering the camera’s pixel clock to 12 MHz (set register 0x11 to 0x01), which drops FPS to 15 but cuts camera power to 45 mA. The display’s backlight can be PWM-controlled down to 10% duty for 20 mA draw, but visibility drops. For thermal management, the STM32F407 can reach 85°C junction temperature without a heatsink, but the display’s backlight LED strip can get warm—keep it below 60°C by limiting current to 100 mA. Use a 0.1 µF ceramic capacitor near each module’s power pin to suppress switching noise.

Wiring and Signal Integrity

Long wires cause signal degradation, especially for the camera’s parallel data bus. Keep the camera module within 10 cm of the MCU, and use twisted-pair wires for the clock lines (PCLK and SCK). The display’s SPI lines should be under 15 cm to avoid reflections at 40 MHz. Use a 33-ohm series resistor on the SCK line to dampen ringing. The camera’s VSYNC and HSYNC are critical—they must be pulled up to 3.3V with 10kΩ resistors to avoid floating. If you use a breadboard, expect noise issues: the camera’s PCLK at 24 MHz can couple into the display’s SPI lines, causing pixel corruption. A 4-layer PCB with ground plane is recommended, but for prototyping, use a shielded cable for the camera data bus and separate the display and camera power traces. The display’s backlight is a PWM-driven LED, which can inject 1 kHz noise into the 3.3V rail—add a 100 µF electrolytic capacitor near the backlight pin to smooth it. For the camera’s I2C lines, keep them under 5 cm and use 2.2kΩ pull-ups instead of 4.7kΩ to reduce rise time at 400 kHz. If you see flickering on the display, it’s often due to the camera’s DMA interrupting the display’s SPI transfer—use a higher priority for the display DMA channel (e.g., DMA2 stream 3 for SPI1) and a lower priority for the camera DMA (DMA2 stream 1 for DCMI).

Real-World Testing and Debugging

When you first power up, the display should show the initialization color (e.g., red, green, blue bars) if the ILI9341 driver is correct. Use a logic analyzer to check the SPI signals: CS should go low for each command, and the clock should be clean. For the camera, use a scope to measure PCLK—it should be a square wave at 24 MHz. If the camera outputs no data, check the I2C communication: the OV2640’s default slave address is 0x60 (write) or 0x61 (read). Send a register read of 0x0A (product ID) and expect 0x26. If you get 0xFF, the I2C lines are swapped or the pull-ups are wrong. Once the camera captures, you’ll see a JPEG stream on the D0-D7 lines—the first byte should be 0xFF. If