Why a GT911 touch controller doesn't respond over I2C
The display lights up, the I2C scan finds nothing, and the touch driver logs an error or just never fires. Or it worked yesterday, and today the same code finds the controller at a different address. If the panel uses a Goodix GT911, the cause is usually not the driver. It is how the reset and interrupt lines were wired and sequenced.
This note covers the failures we would check, in the order we would check them, and what to put in the schematic so the next board does not repeat them.
The INT pin picks the I2C address on every reset
The GT911 has two possible I2C addresses, 0x5D and 0x14. The Goodix programming guide says the host must select one using the INT pin each time the chip powers on or is reset. That is the part people miss. INT is a dual-purpose pin. During reset it is an input the controller samples. Afterward it is the interrupt output.
The Linux kernel binding for Goodix touch controllers describes the same mechanism: the line is sampled by the device on reset deassertion to select the I2C client address, and the host can drive it during the reset sequence. The Zephyr binding is more explicit about the driver side. It holds INT low across the reset sequence so the controller selects its primary address. It also offers an alternate-address option that probes for the device instead, for boards that do not route the pin.
That gives you three real-world situations.
INT and RESET are both wired to the MCU. The firmware drives both in a defined order, so the address is deterministic. This is the design you want.
INT is wired to the MCU, RESET is not. The controller only gets a fresh address decision at power-up. If the MCU resets and restarts without cycling power to the panel, the controller may still be sitting at whatever address it chose earlier, in a state the firmware does not expect. Cold boot works, warm reset does not.
INT is not driven during reset. The address then depends on whatever the board does to that line: a stray pull resistor, leakage, or nothing at all. We have seen the forum version of this. In a recent LVGL forum thread, one reply describes a GT911 whose address-select line was unconnected, so the chip came up at either address unpredictably. The reported workaround was to probe one address and fall back to the other. That thread has no confirmed fix for the original poster, so treat it as a symptom report, not a recipe.
Probing both addresses is a reasonable firmware safety net. It is a poor substitute for a board that decides its own address.
Check the logic level against your driver, not a forum comment
Which INT level selects which address is where copied code goes wrong. A code comment in one project may say the opposite of the Zephyr documentation. Do not trust either on faith.
Do this instead:
- Read the reset timing section of the Goodix guide for your exact module revision. Panel vendors sometimes ship their own variant of the document.
- Put a logic analyzer or scope on RESET, INT, SDA and SCL together. Capture a cold boot, not just a warm one.
- Confirm that INT is at the level you intend when RESET rises, and that it holds there for as long as the timing diagram requires.
- Confirm the address the chip then answers at, with a bare I2C read and no driver in the way.
If INT is at the right level and the chip still does not answer at either address, move on to power and pull-ups. A module that is held in reset, or whose supply rail sags during the backlight turn-on, looks identical to a wrong address from software. Our first power-up checklist covers confirming rails before blaming a driver.
Release INT cleanly after reset
After the address is latched, INT becomes the controller’s interrupt output. The Goodix guide says that when INT is in its input state on the host side, the host should leave it floating, with no internal pull-up or pull-down enabled. A host pin left with a pull resistor active can fight the controller’s output or distort the interrupt edge.
Two firmware habits cause trouble here:
- Reconfiguring INT as an interrupt input with the MCU’s pull-up enabled by default, because the HAL example did.
- Leaving INT as an output after the reset sequence, so the controller and the MCU drive the line against each other.
The Zephyr driver source is a short, readable reference for the order of operations: drive the lines, wait, release INT as a plain input, then enable the interrupt or start polling. Copy the order, not the delay numbers. The delays that matter come from the Goodix timing diagram for your module.
The chip answers, but touches never arrive
Once the controller responds at an address, the next trap is the status register. In the Zephyr driver, the handler reads the status byte at 0x814E, ignores the event if bit 7 is clear, takes the touch count from the low bits, and then writes zero back to the register before reading the point data. The host has to clear that status. If it does not, the controller stops reporting new data, and the symptom is one touch that registers and then nothing.
Common ways firmware breaks this:
- Returning early from the read function on an error and skipping the clear.
- Clearing the register only when the touch count is nonzero, so a release event is never acknowledged.
- Reading the point registers in a separate transaction that fails, then never retrying the clear.
If the interrupt fires once and then goes quiet, look at the clear before anything else.
Do not block inside the LVGL read callback
If the UI uses LVGL, the touch driver connects through a pointer input device. The LVGL pointer documentation shows the read callback filling a data structure with the pressed or released state and the point coordinates. That is all it asks for: a state and a point.
What the callback does with I2C is your decision, and it is where products stutter. A callback that performs a full I2C transaction, with long timeouts, on the same thread that renders the screen will stall rendering whenever the bus is slow or the controller is busy. A better pattern is to service the interrupt in a short handler, run the I2C transaction in a task or a deferred context, store the latest state, and let the callback return it immediately. The details depend on your RTOS and your LVGL locking, so check how your port handles threads before choosing. Our note on choosing a display interface covers where the touch bus sits relative to the display.
If the touch bus is shared with sensors, remember that a stuck bus takes the touch controller down with it. We wrote up that failure in I2C bus stuck low after a reset.
Coordinates and configuration
Two smaller items cause “it responds but feels wrong” reports.
Axes. Touch coordinates and display coordinates are separate. A panel mounted rotated, or a display driver that mirrors the image, needs a matching swap or flip on the touch side. Zephyr exposes inverted and swapped axes as properties of the touch device, which tells you where this belongs: in one place, applied once. Applying a flip in both the touch driver and the LVGL layer cancels it out.
Configuration block. The controller keeps a configuration block with its own version and checksum. The Zephyr driver reads that block, rejects the device if the version byte is zero or the checksum does not match, and recalculates the checksum whenever it changes a setting. If you write settings from your own code, calculate the checksum the way the Goodix guide specifies and set the update flag. Writing bytes without a matching checksum is a quiet way to have changes ignored.
What to put on the next schematic
- Route both RESET and INT to the MCU. Do not rely on the touch panel’s flex cable to set the address.
- Pick GPIOs that are not driven by a boot strap or by another peripheral during reset.
- Give the I2C bus pull-ups sized for the bus, and place a test point on INT and RESET so a probe can reach them.
- Put the touch supply where the MCU can cycle it if you need full recovery from a hung controller.
How we approach it
We start with a capture of RESET, INT and the I2C bus together, fix the sequencing at the board or firmware level, and then check touch behavior with the real enclosure and cover glass. If you send the schematic, the panel’s part number and the firmware’s touch driver, we can tell you which of these causes applies.
If your touch panel is silent or works on some boards and not others, we would review the reset and INT wiring in your schematic and fix it before the next spin.
Tools for this topic
- Voltage dividerVout from Vin and two resistors, or the best standard pairs for a target Vout — with tolerance range and load effect.
- Regulator feedback resistorsFeedback pairs for any Vref, with the Iadj term for adjustable regulators and divider-current guidance.
- ESP32 browser flasherFlash a .bin over USB from the browser. It reads the chip and the file first and refuses firmware built for a different chip, revision or flash size.
Related notes
- Why an ESP32-P4 MIPI DSI display stays blackA bring-up order for an ESP32-P4 MIPI DSI panel: DPHY power, timing, init sequence, touch, and the v3.2 silicon change that affects old boards.
- Logging to an SD card without corrupting it on power lossHow to log to an SD card without corrupting it when power drops: when to sync FAT, where littlefs fits, and the hold-up hardware that matters.
- Why a load cell reading drifts with an HX711Why load cell readings creep or jump with an HX711, and the wiring, reference, filtering and calibration checks that separate real drift from noise.