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
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ jobs:
target: esp32
- path: 'components/esp32-timer-cam/example'
target: esp32
- path: 'components/esp32-p4-function-ev-board/example'
target: esp32p4
- path: 'components/esp-box/example'
target: esp32s3
- path: 'components/event_manager/example'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/upload_components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
components/drv2605
components/encoder
components/esp-box
components/esp32-p4-function-ev-board
components/esp32-timer-cam
components/event_manager
components/expressive_eyes
Expand Down
93 changes: 93 additions & 0 deletions components/display_drivers/include/ek79007.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#pragma once

#include <mutex>

#include "display_drivers.hpp"

namespace espp {
/**
* @brief Display driver for the EK79007 MIPI-DSI display controller.
*
* The EK79007 (e.g. the 7-inch 1024x600 panel on the ESP32-P4-HMI-Subboard) is
* largely driven by its DPI video timing; it only needs a short vendor command
* sequence plus a DSI lane-count command and Sleep-Out. This follows the same
* interface as the other espp display drivers and relies on a lower-level
* transport (MIPI-DSI DBI) to execute write_command.
*
* The initialization sequence here mirrors Espressif's esp_lcd_ek79007 driver.
*/
class Ek79007 : public display_drivers::MipiDbiDisplayDriver {
// EK79007 MADCTL mirror bits (non-standard layout)
static constexpr uint8_t SHLR_BIT = 1 << 0; ///< Source (horizontal) flip -> mirror x
static constexpr uint8_t UPDN_BIT = 1 << 1; ///< Gate (vertical) flip -> mirror y

public:
enum class Command : uint8_t {
nop = 0x00, ///< No Operation
swreset = 0x01, ///< Software Reset
sleep_in = 0x10, ///< Sleep In
sleep_out = 0x11, ///< Sleep Out
display_off = 0x28, ///< Display Off
display_on = 0x29, ///< Display On
caset = 0x2A, ///< Column Address Set
raset = 0x2B, ///< Row Address Set
ramwr = 0x2C, ///< Memory Write
madctl = 0x36, ///< Memory Data Access Control
pad_control = 0xB2, ///< DSI lane configuration
};

/// DSI lane configuration values for the pad_control (0xB2) command
static constexpr uint8_t DSI_2_LANE = 0x10;
static constexpr uint8_t DSI_4_LANE = 0x00;

explicit Ek79007(const display_drivers::Config &config)
: MipiDbiDisplayDriver(config,
{.column_address_command = static_cast<uint8_t>(Command::caset),
.row_address_command = static_cast<uint8_t>(Command::raset),
.memory_write_command = static_cast<uint8_t>(Command::ramwr)}) {}

bool initialize() override {
display_drivers::init_pins(config_.reset_pin, config_.data_command_pin, config_.reset_value);

// This must match Espressif's esp_lcd_ek79007 vendor_specific_init_default
// exactly: the DSI lane-config command, the 0x80-0x86 vendor registers, then
// Sleep-Out. Notably it does NOT send MADCTL or Display-On here (the EK79007
// uses its power-on defaults + the DPI video stream); sending those extra
// commands can leave the panel showing black.
auto init_commands = std::to_array<display_drivers::DisplayInitCmd<>>({
// Configure DSI for 2 data lanes
{static_cast<uint8_t>(Command::pad_control), {DSI_2_LANE}, 0},
// Vendor-specific power/timing setup
{0x80, {0x8B}, 0},
{0x81, {0x78}, 0},
{0x82, {0x84}, 0},
{0x83, {0x88}, 0},
{0x84, {0xA8}, 0},
{0x85, {0xE3}, 0},
{0x86, {0x88}, 0},
// Exit sleep (requires >=120ms before sending further commands)
{static_cast<uint8_t>(Command::sleep_out), {}, 120},
});

send_commands(init_commands);
return true;
}

void set_rotation(const DisplayRotation &rotation) override {
Controller::set_rotation(rotation);
auto data = std::array<uint8_t, 1>{make_madctl()};
std::scoped_lock lock(io_mutex_);
write_command(static_cast<uint8_t>(Command::madctl), data, 0);
}

private:
uint8_t make_madctl() const {
uint8_t value = 0;
if (config_.mirror_x)
value |= SHLR_BIT;
if (config_.mirror_y)
value |= UPDN_BIT;
return value;
}
};
} // namespace espp
24 changes: 24 additions & 0 deletions components/esp32-p4-function-ev-board/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES
"base_component"
"codec"
"display"
"display_drivers"
"gt911"
"i2c"
"input_drivers"
"interrupt"
"led"
"task"
"esp_driver_gpio"
"esp_lcd"
"esp_driver_i2s"
"esp_eth"
"esp_netif"
"esp_event"
"fatfs"
"esp_driver_sdmmc"
"sdmmc"
)
69 changes: 69 additions & 0 deletions components/esp32-p4-function-ev-board/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
menu "ESP32-P4 Function EV Board Configuration"

choice ESP_P4_EV_BOARD_DISPLAY
prompt "HMI subboard display panel"
default ESP_P4_EV_BOARD_DISPLAY_EK79007
help
Select the MIPI-DSI panel attached to the HMI subboard. The BSP does not
auto-detect the panel (runtime DSI-ID probing was removed because it hangs
the boot watchdog on the EK79007), so set this to the panel you have.

config ESP_P4_EV_BOARD_DISPLAY_EK79007
bool "EK79007 (7-inch, 1024x600)"
help
The 7-inch 1024x600 panel that ships with the standard
ESP32-P4-Function-EV-Board kit. Backlight on GPIO26, reset on GPIO27.

config ESP_P4_EV_BOARD_DISPLAY_ILI9881C
bool "ILI9881C (10.1-inch, 800x1280)"
help
The 10.1-inch 800x1280 panel variant. Backlight on GPIO23, reset is not
connected (handled over DSI).
endchoice

config ESP_P4_EV_BOARD_INTERRUPT_STACK_SIZE
int "Interrupt task stack size (bytes)"
default 4096
help
Size of the stack used for the GPIO interrupt handler task (button, etc.).

config ESP_P4_EV_BOARD_TOUCH_TASK_STACK_SIZE
int "Touch polling task stack size (bytes)"
default 4096
help
Size of the stack used for the GT911 touch polling task. Used only in
polling mode (see ESP_P4_EV_BOARD_TOUCH_INTERRUPT).

config ESP_P4_EV_BOARD_TOUCH_INTERRUPT
bool "Use interrupt-driven touch instead of polling"
default n
help
By default the ESP32-P4-HMI-Subboard does not route the GT911 touch INT pin
to the ESP32-P4, so touch is polled in a task. The LCD expansion header does
expose the touch INT pin: if you wire it to a free ESP32-P4 GPIO, enable this
to read the GT911 from a GPIO interrupt instead of polling (lower CPU usage
and latency). The GPIO is set below, and can also be overridden at runtime
via initialize_touch().

config ESP_P4_EV_BOARD_TOUCH_INTERRUPT_GPIO
int "Touch interrupt GPIO"
depends on ESP_P4_EV_BOARD_TOUCH_INTERRUPT
default 33
help
GPIO that the GT911 touch INT pin (from the LCD expansion header) is wired
to. Must be a free GPIO not used by another on-board peripheral.

config ESP_P4_EV_BOARD_AUDIO_TASK_STACK_SIZE
int "Audio task stack size (bytes)"
default 8192
help
Size of the stack used for the audio processing task.

config ESP_P4_EV_BOARD_ETHERNET
bool "Enable Ethernet (IP101 RMII) support"
default y
help
Build the Ethernet (EMAC + IP101 RMII PHY) support in the BSP. Disable to
drop the esp_eth dependency if you do not need wired networking.

endmenu
124 changes: 124 additions & 0 deletions components/esp32-p4-function-ev-board/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# ESP32-P4 Function EV Board

[![Badge](https://components.espressif.com/components/espp/esp32-p4-function-ev-board/badge.svg)](https://components.espressif.com/components/espp/esp32-p4-function-ev-board)

Board Support Package (BSP) for the Espressif **ESP32-P4 Function EV Board** used
together with the **ESP32-P4-HMI-Subboard**.

> [!IMPORTANT]
> **Display jumpers.** On the LCD adapter board, the `RST_LCD` and `PWM` signals
> are broken out on a header and must be wired to the ESP32-P4 main board for the
> display to work: connect **`RST_LCD` → J1 GPIO27** and **`PWM` → J1 GPIO26**
> (these are the LCD reset and backlight-PWM pins this BSP drives). Without these
> jumpers the panel never gets reset and the backlight PWM is unconnected, so the
> screen stays black even though everything initializes. See the
> [user guide](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html)
> for the header pinout.

## Official board documentation

- [ESP32-P4-Function-EV-Board User Guide](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html)
(includes the ESP32-P4-HMI-Subboard)
- [ESP32-P4-Function-EV-Board overview page](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/index.html)
- [Board schematic (PDF)](https://dl.espressif.com/dl/schematics/esp32-p4-function-ev-board-schematics_v1.52.pdf)
- [Espressif reference BSP (`esp-bsp`)](https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_p4_function_ev_board)
- [ESP32-P4 Get Started (ESP-IDF)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/get-started/index.html)

### Display panels (HMI subboard)

Both panels plug into the shared LCD adapter board via its FPC connector:

- [LCD Adapter Board Schematic (PDF)](https://dl.espressif.com/dl/schematics/esp32-p4-function-ev-board-lcd-subboard-schematics.pdf)
- [LCD Adapter Board PCB Layout (PDF)](https://dl.espressif.com/dl/schematics/esp32-p4-function-ev-board-lcd-subboard-pcb-layout.pdf)

**EK79007 — 7", 1024x600** (the panel Espressif ships/documents for this board):

- [Display Datasheet (PDF)](https://dl.espressif.com/dl/schematics/display_datasheet.pdf)
- [EK79007AD display driver chip datasheet (PDF)](https://dl.espressif.com/dl/schematics/display_driver_chip_EK79007AD_datasheet.pdf)
- [EK73217BCGA display driver chip datasheet (PDF)](https://dl.espressif.com/dl/schematics/display_driver_chip_EK73217BCGA_datasheet.pdf)

**ILI9881C — 10.1", 800x1280**: a panel option supported by the BSP. Espressif
does not publish a dedicated LCD-subboard schematic/datasheet for this panel on
this board; refer to the
[esp-bsp `esp_lcd_ili9881c` driver](https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_ili9881c)
for the panel/timing details.

The `espp::Esp32P4FunctionEvBoard` class is a singleton hardware abstraction that
initializes and exposes the board's peripherals:

- **MIPI-DSI display** — Kconfig-selectable **EK79007 (7", 1024x600)** or
**ILI9881C (10.1", 800x1280)** — with LVGL integration and PWM backlight.
- **GT911 capacitive multi-touch** — polled by default (the touch INT pin is not
routed to the ESP32-P4 on this board), or **interrupt-driven** if you wire the
INT pin from the LCD expansion header to a GPIO (see [Touch mode](#touch-mode-polling-vs-interrupt)).
- **ES8311 audio codec** (+ NS4150B speaker amplifier) over I2S for playback.
- **10/100 Ethernet** (EMAC + IP101 RMII PHY) with DHCP.
- **microSD card** (4-bit SDMMC, powered via the on-chip LDO).
- **MIPI-CSI camera** (SC2336/OV5647) — pins/SCCB wired, capture pipeline is a
stub (see Camera below).
- **BOOT button**.

All on-board control lines are direct ESP32-P4 GPIOs (this board has no I/O
expander). The display, touch, and audio codec share a single I2C bus
(`SDA=GPIO7`, `SCL=GPIO8`).

## Display panel selection

The active panel is selected at **compile time via Kconfig**
(*ESP32-P4 Function EV Board Configuration* → display panel: EK79007 1024x600 by
default, or ILI9881C 800x1280). `initialize_lcd()` applies that panel's
resolution, DPI timing, backlight GPIO (26 for EK79007, 23 for ILI9881C), and
reset GPIO, and `get_display_controller()` / `display_width()` /
`display_height()` reflect the configured panel.

> [!NOTE]
> The BSP does **not** auto-detect the attached panel. Runtime probing (reading
> the panel ID over DSI) was tried but removed: the EK79007 does not answer DSI
> reads and the read poll has no timeout, which hung the boot watchdog. This
> matches Espressif's esp-bsp, which also selects the panel via Kconfig. Set the
> Kconfig choice to the panel you have.

## Touch mode (polling vs interrupt)

By default the GT911 is **polled** in a task, because the ESP32-P4-HMI-Subboard
does not route the touch INT pin to the ESP32-P4. The LCD expansion header *does*
expose the INT pin, so you can wire it to a free GPIO and switch to
**interrupt-driven** touch (lower CPU usage and latency):

- **Kconfig** (*ESP32-P4 Function EV Board Configuration*):
- `ESP_P4_EV_BOARD_TOUCH_INTERRUPT` — use interrupt-driven touch instead of polling.
- `ESP_P4_EV_BOARD_TOUCH_INTERRUPT_GPIO` — the GPIO the INT pin is wired to.
- **API**: `initialize_touch(callback, interrupt_pin)`. Pass `GPIO_NUM_NC` to
poll, or a valid GPIO to read the GT911 from an interrupt on that pin. The
default `interrupt_pin` (`touch_interrupt_default`) follows the Kconfig setting,
so an unchanged `initialize_touch(cb)` call uses whichever mode you selected in
Kconfig; pass an explicit GPIO to override at runtime.

## Example

See the [example](./example). It initializes the display + touch, shows a live
on-screen status read-out (panel, touch coordinates, SD, Ethernet IP), and brings
up the SD card, audio, Ethernet, and BOOT button.

## Peripheral status / notes

- **Ethernet**: uses the ESP-IDF internal EMAC with the **generic 802.3 PHY
driver** (`esp_eth_phy_new_generic`) for the IP101. The dedicated
`esp_eth_phy_ip101` driver is a separate registry component on ESP-IDF v6+, and
this BSP builds with the component manager disabled, so the generic 802.3 driver
is used instead — it drives the IP101 on this board. The RMII pinout is the
ESP-IDF ESP32-P4 default (MDC=31, MDIO=52, REF_CLK in=50, TX_EN=49, TXD0=34,
TXD1=35, CRS_DV=28, RXD0=29, RXD1=30; PHY reset=51, addr=1).
- **BOOT button**: GPIO35 is shared with Ethernet **RMII TXD1**, so the button
cannot be used as a runtime input while Ethernet is enabled — claiming the pin
would take down Ethernet TX. `initialize_button()` refuses (returns `false`)
while Ethernet is active.
- **Camera**: the SC2336/OV5647 MIPI-CSI sensor's pins are documented (SCCB on
the internal I2C bus at 0x30, reset/XCLK not connected), but the `esp_video`
capture pipeline is **not yet implemented**; `initialize_camera()` returns
false. Contributions welcome.
- This BSP requires PSRAM for the MIPI-DSI frame buffers (see the example's
`sdkconfig.defaults`).
- The Ethernet RMII pinout comes from the ESP-IDF ESP32-P4 software defaults
(corroborated by the board docs) rather than a parsed schematic — verify
against your board revision if Ethernet does not link.
22 changes: 22 additions & 0 deletions components/esp32-p4-function-ev-board/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.20)

set(ENV{IDF_COMPONENT_MANAGER} "0")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py esp32-p4-function-ev-board display lvgl cdr rtps ping"
CACHE STRING
"List of components to include"
)

project(esp32_p4_function_ev_board_example)

set(CMAKE_CXX_STANDARD 20)
Loading
Loading