From 1116a61459932223336b252e5b6fe3c4f6dc6877 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Tue, 6 Oct 2026 01:18:45 -0500 Subject: [PATCH] Board config: the P4 DEV-KIT with a KeDei 5" DSI display; ft6x36 no longer reads an idle 0xFF status as 15 touches --- .../board_config.py | 75 +++++++++++++++++++ .../board_peripherals.py | 32 ++++++++ .../package.json | 24 ++++++ docs/board-configs.md | 1 + docs/device-matrix.md | 1 + docs/display-boards.md | 1 + docs/touch-drivers.md | 2 +- drivers/touch/ft6x36.py | 6 +- 8 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_config.py create mode 100644 board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_peripherals.py create mode 100644 board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/package.json diff --git a/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_config.py b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_config.py new file mode 100644 index 00000000..16132641 --- /dev/null +++ b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_config.py @@ -0,0 +1,75 @@ +"""Waveshare ESP32-P4-WIFI6-DEV-KIT with a KeDei 5" DSI display (800x480) - MicroPython + +The display is a Raspberry Pi-style DSI panel: a Chipone ICN6211 DSI-to-RGB +bridge that the panel's own microcontroller configures, so nothing here writes +to the bridge. That controller sits at I2C 0x45 and speaks the official Pi +display's protocol (0x85 powers the panel, 0x86 sets the backlight). It also +acknowledges every I2C address, so an I2C scan of this bus is meaningless. The +touch controller is a FocalTech part at 0x38. + +What the bridge needs, found on the bench (2026-10-06): non-burst video with +sync pulses and a continuous HS clock, one lane, and the timing it holds in its +own registers (0x20-0x29). Without non_burst/continuous_clock the panel stays +white; with Linux's TC358762 timing the picture shears. +""" + +import time + +from ft6x36 import FT6x36 +from machine import I2C, Pin + +from displaydev.fbdisplay import FBDisplay + +try: + from mipidsi import Bus, Display +except ImportError as exc: + raise NotImplementedError("MIPI DSI requires displayif mipidsi cmod (esp32p4 port)") from exc + +I2C_SCL = 8 +I2C_SDA = 7 +PANEL_CTRL = 0x45 # the panel's microcontroller +_POWERON = 0x85 +_PWM = 0x86 + +i2c = I2C(1, scl=Pin(I2C_SCL), sda=Pin(I2C_SDA), freq=100_000) + +# Panel off while the DSI link starts; the controller configures the bridge +# when it powers the panel, and that wants the clock already running. +i2c.writeto_mem(PANEL_CTRL, _POWERON, b"\x00") +time.sleep_ms(300) + +display_bus = Bus(frequency=528_000_000, num_lanes=1, ldo_chan=3, ldo_voltage_mv=2500) + +fb = Display( + display_bus, + b"", + width=800, + height=480, + color_depth=16, + pixel_clock_frequency=33_000_000, # 16 bits x 33 MHz = 528 Mbps on one lane + hsync_front_porch=46, + hsync_pulse_width=20, + hsync_back_porch=210, + vsync_front_porch=22, + vsync_pulse_width=10, + vsync_back_porch=23, + non_burst=True, + continuous_clock=True, +) + +i2c.writeto_mem(PANEL_CTRL, _POWERON, b"\x01") +time.sleep_ms(300) +i2c.writeto_mem(PANEL_CTRL, _PWM, b"\xff") # backlight, 0-255 + +touch = FT6x36(i2c, address=0x38) +# The touch panel reads 180 degrees from the display: the default table +# shifted by two rotations. +touch_rotation_table = (0b110, 0b011, 0b000, 0b101) + +display_drv = FBDisplay(fb) + +touch_read = touch.read_points + +from board_peripherals import PERIPHERALS, load_peripherals + +load_peripherals(globals()) diff --git a/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_peripherals.py b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_peripherals.py new file mode 100644 index 00000000..db9499a9 --- /dev/null +++ b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_peripherals.py @@ -0,0 +1,32 @@ +"""Lazy constructors for the DEV-KIT's non-UI devices. PERIPHERALS = lazy roles only. + +Only the C6 radio so far; the board's ES8311 codec, SD slot and camera aren't +described here yet. +""" +import boarddev +import sys + +PERIPHERALS = frozenset({"wlan", "ble"}) + + +def load_peripherals(ns): + boarddev.bind_lazy(ns, sys.modules[__name__]) + + +def wlan(): + import network + + return network.WLAN(network.STA_IF) + + +def ble(): + # A bledev adapter (docs/ble.md §2). It also answers every bluetooth.BLE + # method, so code written for the raw radio keeps working. Without bledev + # (or aioble) installed, the raw radio, as before. + try: + import bledev.mpble + except ImportError: + import bluetooth + + return bluetooth.BLE() + return bledev.mpble.get() diff --git a/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/package.json b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/package.json new file mode 100644 index 00000000..edf91923 --- /dev/null +++ b/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/package.json @@ -0,0 +1,24 @@ +{ + "urls": [ + [ + "board_config.py", + "github:PyDevices/pydevices/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_config.py" + ], + [ + "board_peripherals.py", + "github:PyDevices/pydevices/board_configs/fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi/board_peripherals.py" + ], + [ + "ft6x36.py", + "github:PyDevices/pydevices/drivers/touch/ft6x36.py" + ] + ], + "deps": [ + [ + "pydevices", + "latest" + ] + ], + "version": "0.1", + "notes": "KeDei 5in DSI (ICN6211) on the DEV-KIT's DSI connector; needs displayif with mipidsi non_burst/continuous_clock. Radio only; codec/SD/camera not yet described." +} diff --git a/docs/board-configs.md b/docs/board-configs.md index ecdfa530..e4c6175d 100644 --- a/docs/board-configs.md +++ b/docs/board-configs.md @@ -85,6 +85,7 @@ An exact match for all four is rare; bus + display controller is usually enough |-----------|----------| | `fbdisplay/qualia_tl040hds20` | MicroPython Qualia RGB | | `fbdisplay/esp32-p4-wifi6-touch-lcd-4b` | Waveshare ESP32-P4-WIFI6-Touch-LCD-4B (MIPI-DSI, touch, ES8311) | +| `fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi` | Waveshare ESP32-P4-WIFI6-DEV-KIT with a KeDei 5" Raspberry Pi-style DSI display (ICN6211 bridge, FocalTech touch) | | `fbdisplay/t-rgb_480` | LilyGO T-RGB 480×480 ST7701 (ESP32-S3; RGB via pydevices/displayif) | | `fbdisplay/pico2_dvi_sock_640x480` | Pico 2 + Adafruit DVI Sock or PiCowbell HSTX | | `fbdisplay/pico2w_dvi_sock_640x480` | Pico 2 W + Sock / PiCowbell HSTX | diff --git a/docs/device-matrix.md b/docs/device-matrix.md index 7389a572..931b9587 100644 --- a/docs/device-matrix.md +++ b/docs/device-matrix.md @@ -24,6 +24,7 @@ Boards whose display is the reason to buy them. | Product | `board_config` path | Eager UI | Lazy `PERIPHERALS` | |---|---|---|---| | Waveshare ESP32-P4-WIFI6-Touch-LCD-4B | `fbdisplay/esp32-p4-wifi6-touch-lcd-4b` | `touch` | `audio_out`, `audio_in`, `sdcard`, `camera`, `radio`, `wlan`, `ble`, `usb_device` | +| Waveshare ESP32-P4-WIFI6-DEV-KIT + KeDei 5" DSI | `fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi` | `touch` | `wlan`, `ble` | | Adafruit Qualia S3 + TL040HDS20 | `fbdisplay/qualia_tl040hds20` | `touch`, `keypad`, `io_expander` | `wlan`, `ble` | | Waveshare ESP32-S3-Touch-LCD-4.3 | `fbdisplay/esp32-s3-touch-lcd-4_3` | `touch`, `io_expander` | `sdcard`, `can`, `rs485`, `usb_device`, `wlan`, `ble` | | Waveshare ESP32-S3-Touch-LCD-7 | `fbdisplay/esp32-s3-touch-lcd-7` | `touch`, `io_expander` | `sdcard`, `can`, `rs485`, `usb_device`, `wlan`, `ble` | diff --git a/docs/display-boards.md b/docs/display-boards.md index 2541bf3c..9a3697db 100644 --- a/docs/display-boards.md +++ b/docs/display-boards.md @@ -28,6 +28,7 @@ variant (`C6_WIFI`). | Product / nickname | `board_config` dir | Resolution | Panel / bus | Touch | Expander / IO | |---|---|---|---|---|---| | Waveshare ESP32-P4-WIFI6-Touch-LCD-4B | `esp32-p4-wifi6-touch-lcd-4b` | 720×720 | ST7703 **MIPI DSI** (`mipidsi`) | GT911 | — | +| Waveshare ESP32-P4-WIFI6-DEV-KIT + KeDei 5" DSI | `esp32-p4-wifi6-dev-kit_kedei-5in-dsi` | 800×480 | ICN6211 bridge, **MIPI DSI** (`mipidsi`, `non_burst`, `continuous_clock`) | FocalTech (`ft6x36`) | — | | Adafruit Qualia S3 + TL040HDS20 | `qualia_tl040hds20` (+ CP under `cp/fbdisplay/qualia_tl040hds20`) | 720×720 | RGB-666→565 **DotClock** | FT6x36 @ `0x48` | PCA9554 @ `0x3f` | | Waveshare ESP32-S3-Touch-LCD-4.3 | `esp32-s3-touch-lcd-4_3` | 800×480 | ST7262 RGB **DotClock** | GT911 @ `0x5D` | CH422G | | LILYGO T-RGB 2.1″ round | `t-rgb_480` | 480×480 | ST7701 RGB **DotClock** | CST820 (`cst8xx`) | XL9535 | diff --git a/docs/touch-drivers.md b/docs/touch-drivers.md index 15da9f2e..5e1aa9ad 100644 --- a/docs/touch-drivers.md +++ b/docs/touch-drivers.md @@ -38,7 +38,7 @@ installers already include the drivers they require. | File | Chip | Typical boards | |------|------|----------------| -| `ft6x36.py` | FocalTech FT6x36 | ESP32-S3 dev boards, Wokwi | +| `ft6x36.py` | FocalTech FT6x36 (and the FT5426-family part on a KeDei 5" DSI) | ESP32-S3 dev boards, Wokwi, the P4 DEV-KIT with a KeDei 5" | | `tt21100.py` | TT21100 | PyPortal, FunHouse | | `stmpe610.py` | STMPE610 | PiTFT FeatherWing | | `xpt2046.py` | XPT2046 | Resistive SPI touch | diff --git a/drivers/touch/ft6x36.py b/drivers/touch/ft6x36.py index 37e70588..81403f90 100644 --- a/drivers/touch/ft6x36.py +++ b/drivers/touch/ft6x36.py @@ -93,7 +93,11 @@ def get_gesture(self) -> int: def get_positions(self) -> list: positions = [] - num_points = self._i2c.readfrom_mem(self._address, _TD_STATUS_REG, 1)[0] & 0x0F + status = self._i2c.readfrom_mem(self._address, _TD_STATUS_REG, 1)[0] + # Some FocalTech firmware (an FT5426-family part on a KeDei 5" DSI + # panel) reports 0xFF here with nothing touching; read as 15 points it + # was a permanent phantom press. This driver reads at most two. + num_points = 0 if status == 0xFF else status & 0x0F if num_points > 0: positions.append(self._get_p1()) if num_points > 1: