How to add a backlight control to a 2.4 inch 240x320 TFT display?
How to Add a Backlight Control to a 2.4 inch 240x320 TFT Display
To add backlight control to a 2.4 inch 240x320 tft display, you need to interface the LED backlight anode (typically pin 19 or labeled as LED-A) with a PWM-capable GPIO pin on your microcontroller, via a current-limiting resistor or a dedicated MOSFET driver. The backlight cathode (LED-K, often pin 20) connects to ground. Most of these displays use a 3.3V logic supply for the controller, but the backlight itself runs on a separate voltage—usually 3.0V to 3.3V at 80–120 mA. If you connect LED-A directly to a 3.3V GPIO, you risk pulling too much current (over 20 mA per pin) and damaging the MCU. Instead, use a small N-channel MOSFET like the 2N7002 or a transistor (e.g., 2N2222) to switch the backlight. For finer control, feed a PWM signal from the MCU (e.g., timer output on pin 9 on an Arduino Uno) to the gate of the MOSFET, with a 10kΩ pull-down resistor to ground. The drain connects to LED-A, the source to ground, and the LED-A is also connected to a 3.3V supply through a 10Ω resistor to limit current. This setup lets you adjust brightness from 0% to 100% by varying the PWM duty cycle, typically at 1 kHz to 5 kHz to avoid flicker. The 2.4 inch 240x320 tft display from DisplayModule includes a 4-wire SPI interface plus backlight pins, making it straightforward to integrate with STM32, ESP32, or Raspberry Pi Pico.
Let’s break down the hardware specifics. The backlight in these TFT modules is usually a white LED array with a forward voltage of 3.0V to 3.3V and a forward current of 80 mA to 120 mA. If you run it at 100 mA, the power dissipation is about 0.3W. Many breakout boards include a 100Ω resistor on the backlight circuit, but that’s for direct 5V connection—not for PWM control. If you use a 5V supply, the resistor drops 2V, leaving 3V for the LED, but this wastes power as heat. A better approach: use a 3.3V regulated supply and a 10Ω resistor, which limits current to roughly 30 mA per LED segment (assuming 4 parallel LEDs, total 120 mA). For PWM, the resistor value should be calculated based on the peak current: R = (V_supply – V_led) / I_led. For 3.3V supply and 3.0V LED, with 100 mA, R = 0.3V / 0.1A = 3Ω. But 3Ω resistors are uncommon; use 3.3Ω or 5.1Ω. The power rating: P = I²R = 0.1² × 5.1 = 0.051W, so a 1/4W resistor is fine.
Now, the PWM frequency matters. Too low (below 200 Hz) and you’ll see visible flicker, especially with camera sensors or in low-light conditions. Too high (above 10 kHz) and the MOSFET switching losses increase, plus the LED driver may not respond linearly. I recommend 1 kHz to 2 kHz for most MCUs. For example, on an ESP32, you can use the LEDC library to set up a PWM channel on any GPIO, with 8-bit resolution (0–255) and a frequency of 1 kHz. On a Raspberry Pi Pico, use the PIO-based PWM for up to 10 kHz with 16-bit resolution. On an STM32, use a timer in PWM mode, like TIM2 on PA0, with a prescaler of 72 and counter period of 1000 for 72 MHz clock to get 1 kHz. The duty cycle controls brightness: 0% is off, 50% is half brightness, 100% is full. But note that human perception of brightness is logarithmic, so a linear PWM duty cycle doesn’t feel linear. You’ll need to gamma-correct the duty cycle: output = 255 × (input/255)^(2.2). For example, for 50% perceived brightness, input = 128, output = 255 × (0.5)^2.2 ≈ 255 × 0.217 = 55. So you’d set PWM to 55/255 ≈ 21.6% duty cycle.
Let’s talk about MOSFET selection. The 2N7002 is a common SMD N-channel MOSFET with Vgs(th) of 0.8V to 3V, Rds(on) of about 5Ω at 3.3V gate drive, and max drain current of 115 mA. That’s marginal for 120 mA backlight. A better choice is the SI2302 (Vgs(th) 0.65V, Rds(on) 0.1Ω, 2A rating) or the AO3400 (Vgs(th) 1.2V, Rds(on) 0.03Ω, 5.8A). These are cheap and widely available. For through-hole, use a 2N2222A NPN transistor in a common-emitter configuration: base to PWM via a 1kΩ resistor, collector to LED-A, emitter to ground. But the transistor saturates with a Vce(sat) of 0.3V, so the LED voltage drops by 0.3V, reducing brightness slightly. The MOSFET is better because it acts as a switch with negligible voltage drop. If you use a BJT, add a 10kΩ base-to-emitter resistor to prevent floating.
Now, software implementation on different platforms. On Arduino Uno (ATmega328P), the analogWrite() function on pin 9 or 10 generates PWM at 490 Hz (pin 5 and 6 at 980 Hz). That’s too low for flicker-free operation. To change frequency, you need to set the timer registers. For example, to set Timer1 on pin 9 to 1 kHz: TCCR1B = (TCCR1B & 0xF8) | 0x02; // prescaler 8, then OCR1A = 1999 for 16 MHz / 8 / 2000 = 1 kHz. Then use analogWrite(9, duty) but note that analogWrite uses 8-bit, so duty is 0–255. For 1 kHz, the maximum duty cycle is 255, but the actual frequency is 16 MHz / 8 / (1999+1) = 1 kHz. This works. On ESP32, use the LEDC API: ledcSetup(0, 1000, 8); ledcAttachPin(2, 0); ledcWrite(0, 128); sets channel 0 on GPIO 2 at 1 kHz, 8-bit, 50% duty. On Raspberry Pi Pico, use machine.PWM: pwm = machine.PWM(machine.Pin(2)); pwm.freq(1000); pwm.duty_u16(32768); // 50% duty. On STM32 with HAL, use HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); then __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 500); for 50% duty with a period of 1000.
Let’s address power supply considerations. The backlight draws 80–120 mA. If you’re powering the TFT from a 3.3V regulator (e.g., AMS1117-3.3) on a 5V rail, the regulator must supply both the logic (typically 10–20 mA) and the backlight. Total current: 120 mA + 20 mA = 140 mA. The AMS1117 can handle up to 1A, but it drops 1.3V at 140 mA, dissipating 0.182W, which is fine. However, if you use a 5V supply directly to the backlight via a resistor, you waste power: (5V – 3V) × 0.1A = 0.2W. That’s okay for a prototype, but for battery-powered projects, use a 3.3V supply and a low-dropout regulator. Also, avoid using the same 3.3V rail for the backlight and the MCU if the MCU is sensitive to noise. The backlight PWM switching can cause voltage ripple. Use a 10µF ceramic capacitor near the backlight LED-A pin and a 100µF electrolytic on the 3.3V input to smooth out transients.
One common mistake: connecting the backlight anode to a 5V pin without a resistor. The LED will burn out instantly. Always include a series resistor. Another mistake: using a GPIO pin directly to drive the backlight. A typical GPIO can source only 20 mA (ATmega328P) or 40 mA (ESP32), but the backlight needs 80–120 mA. This will damage the GPIO or cause brownouts. Always use a transistor or MOSFET. Also, some TFT modules have a backlight enable pin (e.g., BL or LED) that is active high. If you connect it to 3.3V, the backlight is always on. To control it, you need to cut the trace on the PCB and add a jumper wire, or use a MOSFET to switch the 3.3V supply. This is common on cheaper modules from AliExpress. Check the datasheet or schematic of your specific module. The DisplayModule version has dedicated LED-A and LED-K pins, so no trace cutting is needed.
Now, let’s talk about brightness control with software. You can implement a smooth fade-in/fade-out effect using a timer interrupt. For example, on Arduino, use a 10 ms timer to increment or decrement the PWM duty cycle by 1 every 10 ms. That gives a 2.55-second fade from off to full. On ESP32, use the FreeRTOS task with vTaskDelay(10 / portTICK_PERIOD_MS). For a more advanced control, you can read a potentiometer or a light sensor to adjust brightness automatically. Connect a 10kΩ potentiometer to an ADC pin (e.g., GPIO34 on ESP32), read the ADC value (0–4095), map it to 0–255, apply gamma correction, and set the PWM duty. This gives a manual brightness knob. For ambient light sensing, use a photoresistor (LDR) with a 10kΩ resistor in a voltage divider, read the ADC, and invert the value: brighter ambient = lower backlight brightness. This saves power in low-light environments.
Let’s examine electrical characteristics of the backlight in detail. The typical LED array has 4 white LEDs in parallel, each with a forward voltage of 3.0V to 3.2V at 20–30 mA. So total current is 80–120 mA. The LED dynamic resistance is about 10Ω per LED, so the parallel combination is 2.5Ω. When you add a series resistor, the total resistance determines the current. For PWM, the average current is duty_cycle × peak_current. If the duty cycle is 50%, the average current is 50 mA, and the brightness is about 50% of full. But because the LED current vs. brightness is linear (unlike human perception), you need to compensate. The typical LED has a luminous intensity of 200–300 mcd at 20 mA. At 100 mA, it’s about 1000–1500 mcd. So full brightness is around 4000–6000 mcd total for the 4 LEDs. At 50% duty, the perceived brightness is about 18% (due to gamma). So to get 50% perceived brightness, you need about 21.6% duty as calculated earlier.
Now, thermal management. The backlight dissipates 0.3W at full brightness. In a closed enclosure, the temperature can rise by 10–20°C above ambient. The LEDs have a maximum junction temperature of 85°C. If you run at 100% duty continuously, ensure adequate ventilation. For battery-powered devices, you can reduce power by limiting the maximum duty cycle to 80% (which reduces brightness by about 30% perceptually but saves 20% power). Also, use a PWM frequency above 1 kHz to avoid audible noise from the inductor in some boost converters if you use a step-up converter for the backlight. Some modules use a boost converter to drive the backlight from a 3.3V supply to 9V for series LEDs. In that case, the PWM should be applied to the enable pin of the boost converter, not directly to the LED. Check the module’s schematic. The DisplayModule 2.4 inch 240x320 tft display uses a direct LED connection, so no boost converter is involved.
Let’s compare different backlight control methods in a table:
| Method | Components | Pros | Cons | Typical Current |
|---|---|---|---|---|
| Direct GPIO + Resistor | GPIO pin, 100Ω resistor | Simple, no extra parts | Exceeds GPIO current limit, may damage MCU | 20 mA max |
| NPN Transistor (2N2222) | 1kΩ base resistor, 10kΩ pull-down | Cheap, through-hole | 0.3V drop, lower max brightness | 100 mA |
| N-Ch MOSFET (2N7002) | 10kΩ gate pull-down | Low voltage drop, fast switching | Marginal for 120 mA, Rds(on) high | 115 mA |
| N-Ch MOSFET (SI2302) | 10kΩ gate pull-down | Low Rds(on), high current rating | Surface mount, harder to solder | 2A |
| PWM with Boost Converter | Boost IC, inductor, capacitor | Can drive series LEDs, higher voltage | Complex, more components | Varies |
For most hobby projects, the SI2302 or AO3400 MOSFET is the best choice. They are available in SOT-23 packages, which can be soldered with a fine tip iron. Use a 10kΩ resistor from gate to ground to keep the MOSFET off when the MCU is in reset. The PWM signal from the MCU should be 3.3V logic. If your MCU runs at 5V (like Arduino Uno), use a 1kΩ series resistor between the GPIO and the gate to limit inrush current, though the gate capacitance is only a few pF, so it’s not critical. But if you use a 5V GPIO, the gate voltage is 5V, which is above the 3.3V LED supply, but the MOSFET will still switch fine because Vgs is 5V, which is within the ±20V limit. However, the LED supply is 3.3V, so the drain voltage is 3.3V, and the MOSFET will be fully on. No issue.
Now, let’s discuss software gamma correction in detail. Create a lookup table (LUT) of 256 values in program memory. For each input brightness value (0–255), the output is 255 × (input/255)^(2.2). For example, input 0: output 0; input 1: output 255 × (1/255)^2.2 ≈ 255 × 0.000015 ≈ 0; input 64: output 255 × (0.25)^2.2 ≈ 255 × 0.043 ≈ 11; input 128: output 55; input 192: output 255 × (0.75)^2.2 ≈ 255 × 0.54 ≈ 138; input 255: output 255. This LUT can be stored as an array of uint8_t. On Arduino, use PROGMEM: const uint8_t gammaLUT[256] PROGMEM = {0, 0, 0, ...}; Then read with pgm_read_byte(&gammaLUT[input]). On ESP32, just use a regular array since it has plenty of RAM. On STM32, store in flash with const uint8_t gammaLUT[256] = { ... }; The LUT uses 256 bytes, which is negligible. Then in your code, when you set brightness, you do: analogWrite(backlightPin, gammaLUT[brightness]); where brightness is 0–255.
Let’s talk about interfacing with the TFT controller. The display uses an ILI9341 or ST7789 controller (check the datasheet). The backlight is independent of the controller. You can control the backlight even if the display is not initialized. But if you want to turn off the backlight completely, set the PWM duty to 0. However, some controllers have a sleep mode that also turns off the backlight. For example, the ILI9341 has a command 0x10 (sleep in) and 0x11 (sleep out). When in sleep, the display draws less than 10 µA, but the backlight is still on if you don’t control it separately. So you need both: send the sleep
This Thursday: The Critic's Cut
One long-form review, one shorter polemic, and what we're reading against — delivered to 62,000 inboxes every Thursday morning. No ads, no denomination, no spin.
Read This Week's Review