From 43645a327cdb29458d368bd95ff4be8a64c49685 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Fri, 6 Nov 2026 03:18:10 -0600 Subject: [PATCH 1/2] Board configs: Arduino GIGA R1 WiFi with the GIGA Display Shield arduino-giga-display-shield sets up the shield's 480x800 ST7701S panel on 2-lane MIPI DSI through displayif's mipidsi, and its GT911 touch on I2C4. The panel's start-up commands are a driver package of their own, giga_display_shield_init, with the register values from Arduino's Zephyr ST7701 driver (Apache-2.0). It needs micropython-pydevices' ARDUINO_GIGA DISPLAY_SHIELD firmware with displayif and LVGL; the Python libraries go in the board's ROMFS. Ran tools/libbuild.py check and installers --check, then the config on the board: lv_test_timer for 60 s with no timer errors and touch reading. --- .../board_config.py | 78 +++++++++++++++++++ .../board_peripherals.py | 19 +++++ .../arduino-giga-display-shield/manifest.py | 16 ++++ .../arduino-giga-display-shield/package.json | 29 +++++++ docs/boards/board-configs.md | 1 + docs/boards/device-matrix.md | 1 + docs/boards/display-boards.md | 14 ++++ drivers/display/giga_display_shield_init.py | 62 +++++++++++++++ drivers/drivers.toml | 5 ++ 9 files changed, 225 insertions(+) create mode 100644 board_configs/fbdisplay/arduino-giga-display-shield/board_config.py create mode 100644 board_configs/fbdisplay/arduino-giga-display-shield/board_peripherals.py create mode 100644 board_configs/fbdisplay/arduino-giga-display-shield/manifest.py create mode 100644 board_configs/fbdisplay/arduino-giga-display-shield/package.json create mode 100644 drivers/display/giga_display_shield_init.py diff --git a/board_configs/fbdisplay/arduino-giga-display-shield/board_config.py b/board_configs/fbdisplay/arduino-giga-display-shield/board_config.py new file mode 100644 index 00000000..406db101 --- /dev/null +++ b/board_configs/fbdisplay/arduino-giga-display-shield/board_config.py @@ -0,0 +1,78 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + +"""Arduino GIGA R1 WiFi with the GIGA Display Shield - MicroPython + +The shield's 480x800 ST7701S panel on 2-lane MIPI DSI, through displayif's +``mipidsi`` (the STM32H747's DSI host and LTDC, scanning a framebuffer in the +GIGA's SDRAM), and its GT911 touch on I2C4. Needs a firmware with displayif +built for the shield (micropython-pydevices' ``ARDUINO_GIGA`` board, +``DISPLAY_SHIELD`` variant). +""" + +import time + +from giga_display_shield_init import GIGA_ST7701_INIT +from gt911 import GT911 +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's mipidsi (stm32 H747 build)") from exc + +DISPLAY_ON = "PC6" +BACKLIGHT = "PB12" +TOUCH_RESET = "PI2" +TOUCH_IRQ = "PI1" +TOUCH_I2C = 4 # SCL PB6, SDA PH12 + +# The panel's power enable and its backlight, both active high. The backlight +# comes on once the panel has its first frame. +display_on = Pin(DISPLAY_ON, Pin.OUT, value=1) +backlight = Pin(BACKLIGHT, Pin.OUT, value=0) +time.sleep_ms(20) + +display_bus = Bus(frequency=500_000_000, num_lanes=2) + +# 40 MHz is the STM32's PLL3 at 320 MHz divided by 8. A line of 864 pixels +# then lasts exactly 1350 DSI lane byte clocks (864 * 62.5 / 40). +fb = Display( + display_bus, + GIGA_ST7701_INIT, + width=480, + height=800, + color_depth=16, + pixel_clock_frequency=40_000_000, + hsync_pulse_width=16, + hsync_back_porch=32, + hsync_front_porch=336, + vsync_pulse_width=4, + vsync_back_porch=50, + vsync_front_porch=20, +) +backlight.value(1) + +display_drv = FBDisplay(fb) + +i2c = I2C(TOUCH_I2C, freq=400_000) +touch = GT911( + i2c, + reset_pin=TOUCH_RESET, + irq_pin=TOUCH_IRQ, + address=0x5D, + width=480, + height=800, + touch_points=5, + reverse_axis=False, +) +touch_read = touch.read_points + +touch_rotation_table = (0, 0, 0, 0) + +from board_peripherals import PERIPHERALS, load_peripherals + +load_peripherals(globals()) diff --git a/board_configs/fbdisplay/arduino-giga-display-shield/board_peripherals.py b/board_configs/fbdisplay/arduino-giga-display-shield/board_peripherals.py new file mode 100644 index 00000000..4279fd98 --- /dev/null +++ b/board_configs/fbdisplay/arduino-giga-display-shield/board_peripherals.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + +"""Lazy constructors for the Arduino GIGA R1 WiFi with the GIGA Display Shield. PERIPHERALS = lazy roles only.""" +import boarddev +import sys + +PERIPHERALS = frozenset({"wlan"}) + + +def load_peripherals(ns): + boarddev.bind_lazy(ns, sys.modules[__name__]) + + +def wlan(): + import network + + return network.WLAN(network.STA_IF) diff --git a/board_configs/fbdisplay/arduino-giga-display-shield/manifest.py b/board_configs/fbdisplay/arduino-giga-display-shield/manifest.py new file mode 100644 index 00000000..fb271a16 --- /dev/null +++ b/board_configs/fbdisplay/arduino-giga-display-shield/manifest.py @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT + +# The arduino-giga-display-shield board package: what mip.install("arduino-giga-display-shield") brings. +metadata( + description="Arduino GIGA R1 WiFi + GIGA Display Shield: 480x800 MIPI DSI, GT911 touch - MicroPython", + version="0.1.0", +) + +require("pydevices") +require("gt911") +require("giga_display_shield_init") + +module("board_config.py") +module("board_peripherals.py") diff --git a/board_configs/fbdisplay/arduino-giga-display-shield/package.json b/board_configs/fbdisplay/arduino-giga-display-shield/package.json new file mode 100644 index 00000000..d9ed4395 --- /dev/null +++ b/board_configs/fbdisplay/arduino-giga-display-shield/package.json @@ -0,0 +1,29 @@ +{ + "urls": [ + [ + "board_config.py", + "github:PyDevices/pydevices/board_configs/fbdisplay/arduino-giga-display-shield/board_config.py" + ], + [ + "board_peripherals.py", + "github:PyDevices/pydevices/board_configs/fbdisplay/arduino-giga-display-shield/board_peripherals.py" + ], + [ + "gt911.py", + "github:PyDevices/pydevices/drivers/touch/gt911.py" + ], + [ + "giga_display_shield_init.py", + "github:PyDevices/pydevices/drivers/display/giga_display_shield_init.py" + ] + ], + "deps": [ + [ + "pydevices", + "latest" + ] + ], + "version": "0.1.0", + "description": "Arduino GIGA R1 WiFi + GIGA Display Shield: 480x800 MIPI DSI, GT911 touch - MicroPython", + "generated": "from manifest.py by tools/libbuild.py installers; edit manifest.py, not this" +} diff --git a/docs/boards/board-configs.md b/docs/boards/board-configs.md index 01defc50..020a1b8d 100644 --- a/docs/boards/board-configs.md +++ b/docs/boards/board-configs.md @@ -124,6 +124,7 @@ An exact match for all four is rare; bus + display controller is usually enough | Directory | Hardware | |-----------|----------| | `fbdisplay/qualia_tl040hds20` | MicroPython Qualia RGB | +| `fbdisplay/arduino-giga-display-shield` | Arduino GIGA R1 WiFi with the GIGA Display Shield (MIPI-DSI, GT911 touch) | | `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) | diff --git a/docs/boards/device-matrix.md b/docs/boards/device-matrix.md index 9eb798ca..f1f91063 100644 --- a/docs/boards/device-matrix.md +++ b/docs/boards/device-matrix.md @@ -23,6 +23,7 @@ Boards whose display is the reason to buy them. | Product | `board_config` path | Eager UI | Lazy `PERIPHERALS` | |---|---|---|---| +| Arduino GIGA R1 WiFi + GIGA Display Shield | `fbdisplay/arduino-giga-display-shield` | `touch` | `wlan` | | Waveshare ESP32-P4-WIFI6-Touch-LCD-4B | `fbdisplay/esp32-p4-wifi6-touch-lcd-4b` | `touch` | `audio_out`, `pcm_out`, `pcm_in`, `audio_power`, `audio_in_power`, `sdcard`, `camera`, `radio`, `wlan`, `ble`, `usb_device`, `boot_button` | | Waveshare ESP32-P4-WIFI6-DEV-KIT + KeDei 5" DSI | `fbdisplay/esp32-p4-wifi6-dev-kit_kedei-5in-dsi` | `touch` | `audio_out`, `pcm_out`, `pcm_in`, `audio_power`, `i2c`, `sdcard`, `camera`, `ethernet`, `radio`, `wlan`, `ble`, `boot_button`, `usb_device` (from the `nodisplay/esp32-p4-wifi6-dev-kit` package) | | Adafruit Qualia S3 + TL040HDS20 | `fbdisplay/qualia_tl040hds20` | `touch`, `keypad`, `io_expander` | `wlan`, `ble` | diff --git a/docs/boards/display-boards.md b/docs/boards/display-boards.md index c54c1840..d12b8030 100644 --- a/docs/boards/display-boards.md +++ b/docs/boards/display-boards.md @@ -30,6 +30,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 | — | +| Arduino GIGA R1 WiFi + GIGA Display Shield | `arduino-giga-display-shield` | 480×800 | ST7701S **MIPI DSI** (`mipidsi`, STM32H747 DSI host + LTDC) | GT911 @ `0x5D` | — | | 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` (+ CP under `cp/fbdisplay/esp32-s3-touch-lcd-4_3`) | 800×480 | ST7262 RGB **DotClock** | GT911 @ `0x5D` | CH422G | @@ -46,6 +47,19 @@ variant (`C6_WIFI`). ## Per-board detail +### Arduino GIGA R1 WiFi + GIGA Display Shield + +- **board_config title:** `Arduino GIGA R1 WiFi with the GIGA Display Shield - MicroPython` +- **Dir:** `arduino-giga-display-shield` +- **Resolution:** 480×800, portrait +- **Display:** `mipidsi.Bus` + `mipidsi.Display`, ST7701S init sequence + (`giga_display_shield_init`), 2-lane DSI at 500 Mbit/s per lane, pixel clock + 40 MHz, framebuffer in the GIGA's SDRAM. Panel power on PC6, backlight on PB12. +- **Touch:** GT911 on I2C4 (SCL PB6, SDA PH12), reset PI2, interrupt PI1 +- **Firmware:** micropython-pydevices' `ARDUINO_GIGA` board with its + `DISPLAY_SHIELD` variant and `--modules displayif,lvgl`. PyDevices' Python + libraries go in the board's ROMFS: they don't fit in the firmware beside LVGL. + ### Waveshare ESP32-P4-WIFI6-Touch-LCD-4B - **board_config title:** `Waveshare ESP32-P4-WIFI6-Touch-LCD-4B - MicroPython` diff --git a/drivers/display/giga_display_shield_init.py b/drivers/display/giga_display_shield_init.py new file mode 100644 index 00000000..f8f8ef19 --- /dev/null +++ b/drivers/display/giga_display_shield_init.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# SPDX-FileCopyrightText: 2025 Arduino SA +# +# SPDX-License-Identifier: Apache-2.0 + +"""Start-up commands for the Arduino GIGA Display Shield's ST7701S panel. + +The shield's 480x800 panel takes these over 2-lane MIPI DSI. Pass +`GIGA_ST7701_INIT` to displayif's ``mipidsi.Display`` as its init sequence: +each entry is a command, its parameter count (``| 0x80`` when a delay in +milliseconds follows the parameters, 255 meaning 500 ms), then the parameters. + +The register values are Arduino's, from Zephyr's ST7701 driver +(``drivers/display/display_st7701.c``), its devicetree binding's defaults +(``dts/bindings/display/sitronix,st7701.yaml``) and its GIGA Display Shield +overlay (``boards/shields/arduino_giga_display_shield``), Copyright (c) 2025 Arduino SA, +under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0). +They are the same as ArduinoCore-mbed's ``Arduino_H7_Video`` library sends. +""" + +GIGA_ST7701_INIT = ( + b"\x01\x80\xc8" # soft reset, 200 ms + b"\x11\x80\xff" # sleep out, 500 ms + b"\x00\x80\xff" # nop, 500 ms more (Arduino waits 800 ms after sleep out) + # Command2 BK0 + b"\xff\x05\x77\x01\x00\x00\x10" + b"\xc0\x02\x63\x00" + b"\xc1\x02\x11\x02" + b"\xc2\x02\x01\x08" + b"\xcc\x01\x18" + b"\xb0\x10\x40\xc9\x91\x0d\x12\x07\x02\x09\x09\x1f\x04\x50\x0f\xe4\x29\xdf" + b"\xb1\x10\x40\xcb\xd0\x11\x92\x07\x00\x08\x07\x1c\x06\x53\x12\x63\xeb\xdf" + # Command2 BK1: power + b"\xff\x05\x77\x01\x00\x00\x11" + b"\xb0\x01\x65" + b"\xb1\x01\x34" + b"\xb2\x01\x87" + b"\xb3\x01\x80" + b"\xb5\x01\x49" + b"\xb7\x01\x85" + b"\xb8\x01\x20" + b"\xb9\x01\x10" + b"\xc1\x01\x78" + b"\xc2\x01\x78" + b"\xd0\x81\x88\x64" + # GIP + b"\xe0\x03\x00\x00\x02" + b"\xe1\x0b\x08\x00\x0a\x00\x07\x00\x09\x00\x00\x33\x33" + b"\xe2\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\xe3\x04\x00\x00\x33\x33" + b"\xe4\x02\x44\x44" + b"\xe5\x10\x0e\x60\xa0\xa0\x10\x60\xa0\xa0\x0a\x60\xa0\xa0\x0c\x60\xa0\xa0" + b"\xe6\x04\x00\x00\x33\x33" + b"\xe7\x02\x44\x44" + b"\xe8\x10\x0d\x60\xa0\xa0\x0f\x60\xa0\xa0\x09\x60\xa0\xa0\x0b\x60\xa0\xa0" + b"\xeb\x07\x02\x01\xe4\xe4\x44\x00\x40" + b"\xec\x02\x02\x01" + b"\xed\x10\xab\x89\x76\x54\x01\xff\xff\xff\xff\xff\xff\x10\x45\x67\x98\xba" + # Command2 off, display on + b"\xff\x05\x77\x01\x00\x00\x00" + b"\x29\x80\xc8" +) diff --git a/drivers/drivers.toml b/drivers/drivers.toml index cac9df81..14f14f34 100644 --- a/drivers/drivers.toml +++ b/drivers/drivers.toml @@ -129,6 +129,11 @@ version = "0.1.0" description = "GC9D01 display driver" modules = ["display/gc9d01.py"] +[giga_display_shield_init] +version = "0.1.0" +description = "ST7701S start-up commands for the Arduino GIGA Display Shield (MIPI DSI)" +modules = ["display/giga_display_shield_init.py"] + [gpio_pin] version = "0.1.0" description = "Pin shim the i80bus fallback drives" From 4e43f755600f21a5e848b6057d8a022edecafab8 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Fri, 6 Nov 2026 03:27:28 -0600 Subject: [PATCH 2/2] Board docs: the GIGA Display Shield's ROMFS recipe --- docs/boards/display-boards.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/boards/display-boards.md b/docs/boards/display-boards.md index d12b8030..e5c55e5f 100644 --- a/docs/boards/display-boards.md +++ b/docs/boards/display-boards.md @@ -57,8 +57,26 @@ variant (`C6_WIFI`). 40 MHz, framebuffer in the GIGA's SDRAM. Panel power on PC6, backlight on PB12. - **Touch:** GT911 on I2C4 (SCL PB6, SDA PH12), reset PI2, interrupt PI1 - **Firmware:** micropython-pydevices' `ARDUINO_GIGA` board with its - `DISPLAY_SHIELD` variant and `--modules displayif,lvgl`. PyDevices' Python - libraries go in the board's ROMFS: they don't fit in the firmware beside LVGL. + `DISPLAY_SHIELD` variant and `--modules displayif,lvgl` + ([flashing it](https://github.com/PyDevices/micropython-pydevices/blob/main/docs/guides/flash-a-board.md)). +- **Libraries:** the OmniPython libraries don't fit in the firmware beside + LVGL, so they go in the board's 4 MB ROMFS with the two drivers this board + needs. A ROMFS deploy replaces the whole partition, so put them in one folder + and deploy it once, from a clone of this repository: + + ```bash + git clone --depth 1 https://github.com/PyDevices/pydevices.git + mkdir romfs + cp -r pydevices/lib/* pydevices/drivers/touch/gt911.py \ + pydevices/drivers/display/giga_display_shield_init.py romfs/ + mpremote romfs deploy romfs + cd pydevices/board_configs/fbdisplay/arduino-giga-display-shield + mpremote cp board_config.py board_peripherals.py :/flash/ + ``` + + With the `mpy-cross` Python package installed, mpremote compiles the folder + to `.mpy` on the way; without it the board compiles the `.py` files as + they're imported. `/rom` comes before `/flash` on `sys.path`. ### Waveshare ESP32-P4-WIFI6-Touch-LCD-4B