Skip to content

Add LTDC display and GT911 touch drivers for STM32H7 boards#407

Open
nuraci wants to merge 2 commits intolvgl:masterfrom
Copper-And-Code:master
Open

Add LTDC display and GT911 touch drivers for STM32H7 boards#407
nuraci wants to merge 2 commits intolvgl:masterfrom
Copper-And-Code:master

Conversation

@nuraci
Copy link
Copy Markdown

@nuraci nuraci commented Mar 29, 2026

Summary

Add a generic LTDC display driver and GT911 I2C touch driver for STM32H7,
along with board-specific BSP files for three boards (DEV190806042,
FK743M5-XIH6, STM32H7_CORE).

LTDC driver (driver/stm32/st_ltdc/)

  • st_ltdc.c — MicroPython C module exposing the st_ltdc.LTDC class:
    configurable timing parameters, polarity flags, RGB565/RGB888/ARGB8888
    color formats, double-buffered VSYNC-synced flush (DIRECT/FULL mode)
    and DMA2D-accelerated partial updates (PARTIAL mode).
    Framebuffer MPU region is configured as Write-Through so LTDC reads
    are always coherent without manual cache maintenance.
    AXI QoS priorities are tuned to prevent LTDC starvation.
  • touch_i2c.c — MicroPython C module exposing touch_i2c.TP class
    for GT9xx touch controllers, supporting both hardware I2C and SoftI2C.
  • Python layerst_ltdc_utils.py (init helper), boards.py (pin
    configs per board), panels.py (display timing presets), display_conf.py
    (auto-selects board/panel from build config), display_driver.py
    (one-line boot).

Board-specific BSP (driver/stm32/{BOARD}/bsp_ltdc.c)

Each file implements HAL_LTDC_MspInit() / HAL_LTDC_MspDeInit() with
the correct PLL3 pixel-clock setup and GPIO alternate-function mapping
for that board's LTDC wiring. The build system (micropython.mk)
automatically picks the right BSP based on the BOARD variable.

Supported configurations

Board Display Touch I2C
DEV190806042 RGB 24-bit (Port I/J/K) GT911 SoftI2C
FK743M5-XIH6 RGB 24-bit (mixed ports) GT911 SoftI2C
STM32H7_CORE V1.0 RGB (scattered pins) GT911 HW I2C4
STM32H7_CORE V1.3 RGB (scattered pins) GT911 SoftI2C

Test plan

  • Build with LVGL enabled for each board variant
  • Verify display initializes and shows LVGL content without flickering
  • Verify touch input works (GT911 detected, coordinates reported)
  • Verify double-buffered DIRECT mode (no tearing)
  • Verify PARTIAL render mode with DMA2D
  • Test soft-reset cycle (Ctrl-D): display deinits cleanly and reinits
  • Test lv.deinit() / lv.init() cycle without crash

Boards docs and photos

https://github.com/Copper-And-Code/stm32h7-boards-docs

micropython sample code and tools

https://github.com/Copper-And-Code/lv_micropython_code_and_tools


Summary by cubic

Add a generic LTDC display driver and GT911 I2C touch driver for STM32H7 boards with LVGL integration, plus BSPs for DEV190806042, FK743M5-XIH6, and STM32H7_CORE. Enables tear‑free double buffering, VSYNC‑synced flush, and DMA2D‑accelerated partial updates.

  • New Features

    • st_ltdc.LTDC MicroPython driver: display timings, polarity, RGB565/RGB888/ARGB8888, DIRECT/FULL/PARTIAL modes.
    • Framebuffer set write‑through via MPU; AXI QoS tuned to avoid LTDC starvation.
    • touch_i2c.TP for GT9xx (GT911) via hardware I2C or SoftI2C.
    • Board BSPs configure PLL3 pixel clock and GPIOs; build auto-selects by BOARD.
    • Helpers and config: boards.py, panels.py, display_conf.py, st_ltdc_utils.py, display_driver.py; LVGL lv_conf.h added; examples/advanced_demo.py updated.
  • Migration

    • Set BOARD and BOARD_VARIANT for the build; choose PANEL if needed; build with LVGL enabled.
    • In your app: from display_driver import disp, touch (one‑line init).
    • For custom hardware, add entries in boards.py and panels.py.

Written for commit 54f0af9. Summary will update on new commits.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 14 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="driver/stm32/st_ltdc/modules/st_ltdc.c">

<violation number="1" location="driver/stm32/st_ltdc/modules/st_ltdc.c:264">
P2: Backlight selector fields are not fully initialized before use, so init/deinit may read indeterminate `bl_pin`/`bl_led` values and take the wrong backlight path.</violation>

<violation number="2" location="driver/stm32/st_ltdc/modules/st_ltdc.c:482">
P2: `deinit()` lacks an initialization-state guard and unconditionally calls LTDC HAL operations, allowing out-of-sequence calls to operate on an uninitialized `hltdc` handle.</violation>

<violation number="3" location="driver/stm32/st_ltdc/modules/st_ltdc.c:520">
P2: LTDC timing and geometry inputs are not range-validated, allowing unsigned underflow/wrap in register calculations.</violation>
</file>

<file name="driver/stm32/st_ltdc/display_conf.py">

<violation number="1" location="driver/stm32/st_ltdc/display_conf.py:13">
P2: Unsupported BOARD/VERSION/PANEL values can leave `board`/`panel` undefined, causing runtime import failure instead of a clear configuration error.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}
// Blank the layer (alpha=0) so the panel goes dark without stopping clocks.
// Use NoReload + VBL reload to avoid tearing on the final frame.
HAL_LTDC_SetAlpha_NoReload(&hltdc, 0, LTDC_LAYER_1);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: deinit() lacks an initialization-state guard and unconditionally calls LTDC HAL operations, allowing out-of-sequence calls to operate on an uninitialized hltdc handle.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At driver/stm32/st_ltdc/modules/st_ltdc.c, line 482:

<comment>`deinit()` lacks an initialization-state guard and unconditionally calls LTDC HAL operations, allowing out-of-sequence calls to operate on an uninitialized `hltdc` handle.</comment>

<file context>
@@ -0,0 +1,629 @@
+    }
+    // Blank the layer (alpha=0) so the panel goes dark without stopping clocks.
+    // Use NoReload + VBL reload to avoid tearing on the final frame.
+    HAL_LTDC_SetAlpha_NoReload(&hltdc, 0, LTDC_LAYER_1);
+    HAL_LTDC_Reload(&hltdc, LTDC_RELOAD_VERTICAL_BLANKING);
+    if (HAL_LTDC_DeInit(&hltdc) != HAL_OK) {
</file context>
Fix with Cubic

? LTDC_PCPOLARITY_IPC // inverted
: LTDC_PCPOLARITY_IIPC; // not inverted
// Horizontal timing
hltdc.Init.HorizontalSync = self->hsync - 1;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: LTDC timing and geometry inputs are not range-validated, allowing unsigned underflow/wrap in register calculations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At driver/stm32/st_ltdc/modules/st_ltdc.c, line 520:

<comment>LTDC timing and geometry inputs are not range-validated, allowing unsigned underflow/wrap in register calculations.</comment>

<file context>
@@ -0,0 +1,629 @@
+                                ? LTDC_PCPOLARITY_IPC   // inverted
+                                : LTDC_PCPOLARITY_IIPC; // not inverted
+    // Horizontal timing
+    hltdc.Init.HorizontalSync     = self->hsync - 1;
+    hltdc.Init.AccumulatedHBP     = self->hsync + self->hbp - 1;
+    hltdc.Init.AccumulatedActiveW = self->hsync + self->hbp + self->width - 1;
</file context>
Fix with Cubic

// NULL and returns without dereferencing freed memory.
MP_STATE_PORT(current_ltdc_obj) = MP_OBJ_NULL;

ltdc_display_obj_t *self = m_new_obj(ltdc_display_obj_t);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Backlight selector fields are not fully initialized before use, so init/deinit may read indeterminate bl_pin/bl_led values and take the wrong backlight path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At driver/stm32/st_ltdc/modules/st_ltdc.c, line 264:

<comment>Backlight selector fields are not fully initialized before use, so init/deinit may read indeterminate `bl_pin`/`bl_led` values and take the wrong backlight path.</comment>

<file context>
@@ -0,0 +1,629 @@
+    // NULL and returns without dereferencing freed memory.
+    MP_STATE_PORT(current_ltdc_obj) = MP_OBJ_NULL;
+
+    ltdc_display_obj_t *self = m_new_obj(ltdc_display_obj_t);
+    self->base.type = type;
+
</file context>
Fix with Cubic

except ImportError:
PANEL = "IPS1024x600"

if BOARD == "STM32H7_CORE":
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unsupported BOARD/VERSION/PANEL values can leave board/panel undefined, causing runtime import failure instead of a clear configuration error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At driver/stm32/st_ltdc/display_conf.py, line 13:

<comment>Unsupported BOARD/VERSION/PANEL values can leave `board`/`panel` undefined, causing runtime import failure instead of a clear configuration error.</comment>

<file context>
@@ -0,0 +1,26 @@
+except ImportError:
+    PANEL = "IPS1024x600"
+
+if BOARD == "STM32H7_CORE":
+    if VERSION == "V10":
+        from boards import STM32H743_CORE_V10 as board
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant