Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- esp32p4 pins and core command added.
- Removed the modem peripheral for esp32p4
- LDO support for esp32p4
- LCD support for esp32p4
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps:

  • LCD driver support
  • DSI peripheral support with the LCD driver for esp32p4


### Fixed
- Fix pcnt_rotary_encoder example for esp32
Expand Down
124 changes: 124 additions & 0 deletions examples/lcd_dsi_basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//! Basic LCD DSI example for ESP32-P4
//!
//! This example shows how to:
//! - Configure the LCD peripheral for a DSI panel
//! - Create a vendor-specific control panel (ILI9881C over DBI)
//! - Initialize the panel
//! - Create a DPI panel for frame updates
//! - Draw a simple framebuffer
//!
//! Requirements:
//! - Target: ESP32-P4 (`ESP_IDF_TARGET=esp32p4`)
//! - ESP-IDF must have the ILI9881C DSI panel component enabled
//! - A compatible 1024×600 MIPI-DSI display connected to the LCD peripheral
#![allow(unknown_lints)]
#![allow(unexpected_cfgs)]

#[cfg(esp32p4)]
mod example {
use std::time::Duration;

use esp_idf_hal::lcd::*;
use esp_idf_hal::ldo::*;
use esp_idf_hal::peripherals::Peripherals;

use esp_idf_sys::*;

pub fn run() -> anyhow::Result<()> {
// Link ESP-IDF patches (required for examples)
esp_idf_hal::sys::link_patches();

let peripherals = Peripherals::take()?;

// Configure LDO3 for MIPI D-PHY supply (typically 2.5 V).
//
// NOTE: Adjust the voltage to match your board's design and panel
// requirements. 2.5 V is a common value for the DSI PHY supply.
let ldo_cfg = LdoChannelConfig::new(2500);
let _ldo3 = LdoChannel::new(&peripherals.ldo3, &ldo_cfg)?;

// Give the LDO some time to stabilize before enabling the panel.
std::thread::sleep(Duration::from_millis(20));

// Configure video timing for a 1024×600 panel.
// Adjust these values to match your panel's datasheet.
let video_timing = VideoTiming::new(1024, 600)
.hsync_back_porch(100)
.hsync_pulse_width(100)
.hsync_front_porch(100)
.vsync_back_porch(10)
.vsync_pulse_width(10)
.vsync_front_porch(10);

// High-level LCD configuration:
// - 2 DSI data lanes at 1250 Mbps
// - RGB888 pixel format
// - DPI clock at 48 MHz
let config = LcdConfig::new(video_timing)
.num_data_lanes(2)
.lane_bit_rate_mbps(1250)
.dpi_clock_freq_mhz(48)
.pixel_format(PixelFormat::Rgb888);

// Create driver with DSI bus and DBI IO
let mut lcd = LcdDriver::new(peripherals.lcd, &config)?;

// Create vendor control panel (e.g., ILI9881C) over DBI.
//
// Note: This requires the corresponding ESP-IDF component
// (e.g., ILI9881C DSI panel driver) to be enabled.
let mut dev_config: esp_lcd_panel_dev_config_t = lcd.config().into();

// Configure reset GPIO if your panel uses one (adjust as needed).
// Use -1 for no reset GPIO.
dev_config.reset_gpio_num = -1;

let mut control_panel: esp_lcd_panel_handle_t = core::ptr::null_mut();
unsafe {
// Replace `esp_lcd_new_panel_ili9881c` with the appropriate
// vendor-specific constructor for your panel if needed.
esp!(esp_lcd_new_panel_ili9881c(
lcd.dbi_io_handle(),
&dev_config,
&mut control_panel
))?;
}
lcd.set_control_panel(control_panel)?;

// Initialize the control panel
lcd.reset()?;
lcd.init()?;
lcd.set_display_on(true)?;

// Create the DPI panel for pixel data transfers
lcd.create_dpi_panel()?;

// Simple test framebuffer: clear screen to black.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it is fair to call the bitmap you use here a "framebuffer".

A "framebuffer" is something else: in drivers that do support/need framebuffers, this is a region of memory, where a simple write of one or more bytes gets immediately (or almost immediately) reflected on the actual screen.

In fact, one of my biggest concerns below is regarding the usage of a hidden (large) framebuffer by the LCD driver itself.

//
// NOTE:
// - draw_bitmap uses half-open intervals [x1, x2) × [y1, y2)
// - For a 1024×600 display, use x2=1024, y2=600 (NOT 1023, 599)
// - For RGB888, 3 bytes per pixel
let framebuffer = vec![0u8; 1024 * 600 * 3];

lcd.draw_bitmap(0, 0, 1024, 600, &framebuffer)?;

// Keep the application alive so the display remains on
loop {
std::thread::sleep(Duration::from_secs(1));
}
}
}

#[cfg(not(esp32p4))]
mod example {
pub fn run() -> anyhow::Result<()> {
println!("This example is not supported on this target");
Ok(())
}
}

fn main() -> anyhow::Result<()> {
example::run()
}
Loading