Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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())
Original file line number Diff line number Diff line change
@@ -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)
16 changes: 16 additions & 0 deletions board_configs/fbdisplay/arduino-giga-display-shield/manifest.py
Original file line number Diff line number Diff line change
@@ -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")
29 changes: 29 additions & 0 deletions board_configs/fbdisplay/arduino-giga-display-shield/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
1 change: 1 addition & 0 deletions docs/boards/board-configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
1 change: 1 addition & 0 deletions docs/boards/device-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
32 changes: 32 additions & 0 deletions docs/boards/display-boards.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -46,6 +47,37 @@ 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`
([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

- **board_config title:** `Waveshare ESP32-P4-WIFI6-Touch-LCD-4B - MicroPython`
Expand Down
62 changes: 62 additions & 0 deletions drivers/display/giga_display_shield_init.py
Original file line number Diff line number Diff line change
@@ -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"
)
5 changes: 5 additions & 0 deletions drivers/drivers.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading