use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
extern crate alloc;
use alloc::boxed::Box;
use crate::sys::EspError;
use esp_idf_sys::*;
pub type DsiBusHandle = esp_lcd_dsi_bus_handle_t;
pub type PanelIoHandle = esp_lcd_panel_io_handle_t;
pub type PanelHandle = esp_lcd_panel_handle_t;
pub mod config {
use super::*;
pub struct LcdConfig {
pub(crate) bus_config: esp_lcd_dsi_bus_config_t,
pub(crate) dbi_config: esp_lcd_dbi_io_config_t,
pub(crate) dpi_config: esp_lcd_dpi_panel_config_t,
pixel_format: PixelFormat,
}
impl LcdConfig {
#[allow(clippy::unnecessary_cast)]
pub fn new(
video_timing: VideoTiming,
num_data_lanes: u8,
lane_bit_rate_mbps: u32,
dpi_clock_freq_mhz: u32,
pixel_format: PixelFormat,
) -> Self {
let bus_config = esp_lcd_dsi_bus_config_t {
bus_id: 0,
num_data_lanes,
phy_clk_src: soc_module_clk_t_SOC_MOD_CLK_PLL_F160M as u32,
lane_bit_rate_mbps: lane_bit_rate_mbps as _,
};
let dbi_config = esp_lcd_dbi_io_config_t {
virtual_channel: 0,
lcd_cmd_bits: 8,
lcd_param_bits: 8,
};
let color_format: lcd_color_format_t = pixel_format.to_color_format();
let esp_timing: esp_lcd_video_timing_t = video_timing.into();
let mut dpi_config = esp_lcd_dpi_panel_config_t {
virtual_channel: 0,
dpi_clk_src: soc_periph_mipi_dsi_dpi_clk_src_t_MIPI_DSI_DPI_CLK_SRC_DEFAULT,
pixel_format: pixel_format.to_rgb_pixel_format(),
in_color_format: color_format,
out_color_format: color_format,
num_fbs: 1,
video_timing: esp_timing,
dpi_clock_freq_mhz: dpi_clock_freq_mhz as _,
..unsafe { core::mem::zeroed() }
};
dpi_config.flags.set_use_dma2d(1);
Self {
bus_config,
dbi_config,
dpi_config,
pixel_format,
}
}
#[must_use]
pub fn bus_id(mut self, bus_id: i32) -> Self {
self.bus_config.bus_id = bus_id;
self
}
#[must_use]
pub fn phy_clk_src(mut self, phy_clk_src: u32) -> Self {
self.bus_config.phy_clk_src = phy_clk_src;
self
}
#[must_use]
pub fn virtual_channel(mut self, virtual_channel: u8) -> Self {
self.dbi_config.virtual_channel = virtual_channel;
self.dpi_config.virtual_channel = virtual_channel;
self
}
#[must_use]
pub fn lcd_cmd_bits(mut self, lcd_cmd_bits: i32) -> Self {
self.dbi_config.lcd_cmd_bits = lcd_cmd_bits;
self
}
#[must_use]
pub fn lcd_param_bits(mut self, lcd_param_bits: i32) -> Self {
self.dbi_config.lcd_param_bits = lcd_param_bits;
self
}
#[must_use]
pub fn dpi_clk_src(mut self, dpi_clk_src: u32) -> Self {
self.dpi_config.dpi_clk_src = dpi_clk_src;
self
}
#[must_use]
pub fn num_fbs(mut self, num_fbs: u8) -> Self {
self.dpi_config.num_fbs = num_fbs;
self
}
#[must_use]
pub fn use_dma2d(mut self, use_dma2d: bool) -> Self {
self.dpi_config.flags.set_use_dma2d(use_dma2d as u32);
self
}
pub fn bus_config(&self) -> &esp_lcd_dsi_bus_config_t {
&self.bus_config
}
pub fn dbi_config(&self) -> &esp_lcd_dbi_io_config_t {
&self.dbi_config
}
pub fn dpi_config(&self) -> &esp_lcd_dpi_panel_config_t {
&self.dpi_config
}
pub fn pixel_format(&self) -> PixelFormat {
self.pixel_format
}
}
impl Clone for LcdConfig {
fn clone(&self) -> Self {
Self {
bus_config: self.bus_config,
dbi_config: self.dbi_config,
dpi_config: self.dpi_config,
pixel_format: self.pixel_format,
}
}
}
impl core::fmt::Debug for LcdConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LcdConfig")
.field("pixel_format", &self.pixel_format)
.field("num_data_lanes", &self.bus_config.num_data_lanes)
.field("lane_bit_rate_mbps", &self.bus_config.lane_bit_rate_mbps)
.field("dpi_clock_freq_mhz", &self.dpi_config.dpi_clock_freq_mhz)
.field("h_size", &self.dpi_config.video_timing.h_size)
.field("v_size", &self.dpi_config.video_timing.v_size)
.finish_non_exhaustive()
}
}
impl From<&LcdConfig> for esp_lcd_panel_dev_config_t {
fn from(config: &LcdConfig) -> Self {
let bits_per_pixel = config.pixel_format.bits_per_pixel();
let rgb_order = config.pixel_format.rgb_element_order();
let mut panel_config: esp_lcd_panel_dev_config_t = unsafe { core::mem::zeroed() };
panel_config.bits_per_pixel = bits_per_pixel;
panel_config.__bindgen_anon_1.rgb_ele_order = rgb_order.into();
panel_config
}
}
#[derive(Debug, Clone, Copy)]
pub struct VideoTiming {
pub h_size: u32,
pub v_size: u32,
pub hsync_back_porch: u32,
pub hsync_pulse_width: u32,
pub hsync_front_porch: u32,
pub vsync_back_porch: u32,
pub vsync_pulse_width: u32,
pub vsync_front_porch: u32,
}
impl VideoTiming {
pub const fn new(h_size: u32, v_size: u32) -> Self {
Self {
h_size,
v_size,
hsync_back_porch: 0,
hsync_pulse_width: 0,
hsync_front_porch: 0,
vsync_back_porch: 0,
vsync_pulse_width: 0,
vsync_front_porch: 0,
}
}
#[must_use]
pub fn hsync_back_porch(mut self, hsync_back_porch: u32) -> Self {
self.hsync_back_porch = hsync_back_porch;
self
}
#[must_use]
pub fn hsync_pulse_width(mut self, hsync_pulse_width: u32) -> Self {
self.hsync_pulse_width = hsync_pulse_width;
self
}
#[must_use]
pub fn hsync_front_porch(mut self, hsync_front_porch: u32) -> Self {
self.hsync_front_porch = hsync_front_porch;
self
}
#[must_use]
pub fn vsync_back_porch(mut self, vsync_back_porch: u32) -> Self {
self.vsync_back_porch = vsync_back_porch;
self
}
#[must_use]
pub fn vsync_pulse_width(mut self, vsync_pulse_width: u32) -> Self {
self.vsync_pulse_width = vsync_pulse_width;
self
}
#[must_use]
pub fn vsync_front_porch(mut self, vsync_front_porch: u32) -> Self {
self.vsync_front_porch = vsync_front_porch;
self
}
}
impl From<VideoTiming> for esp_lcd_video_timing_t {
fn from(timing: VideoTiming) -> Self {
esp_lcd_video_timing_t {
h_size: timing.h_size,
v_size: timing.v_size,
hsync_back_porch: timing.hsync_back_porch,
hsync_pulse_width: timing.hsync_pulse_width,
hsync_front_porch: timing.hsync_front_porch,
vsync_back_porch: timing.vsync_back_porch,
vsync_pulse_width: timing.vsync_pulse_width,
vsync_front_porch: timing.vsync_front_porch,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PixelFormat {
Rgb888,
Bgr888,
Rgb565,
Bgr565,
Rgb666,
Bgr666,
}
impl PixelFormat {
pub const fn bits_per_pixel(&self) -> u32 {
match self {
PixelFormat::Rgb888 | PixelFormat::Bgr888 => 24,
PixelFormat::Rgb565 | PixelFormat::Bgr565 => 16,
PixelFormat::Rgb666 | PixelFormat::Bgr666 => 18,
}
}
pub const fn bytes_per_pixel(&self) -> usize {
match self {
PixelFormat::Rgb888
| PixelFormat::Bgr888
| PixelFormat::Rgb666
| PixelFormat::Bgr666 => 3,
PixelFormat::Rgb565 | PixelFormat::Bgr565 => 2,
}
}
pub const fn rgb_element_order(&self) -> RgbElementOrder {
match self {
PixelFormat::Rgb888 | PixelFormat::Rgb565 | PixelFormat::Rgb666 => {
RgbElementOrder::Rgb
}
PixelFormat::Bgr888 | PixelFormat::Bgr565 | PixelFormat::Bgr666 => {
RgbElementOrder::Bgr
}
}
}
pub fn to_rgb_pixel_format(&self) -> lcd_color_rgb_pixel_format_t {
match self {
PixelFormat::Rgb888 | PixelFormat::Bgr888 => {
lcd_color_rgb_pixel_format_t_LCD_COLOR_PIXEL_FORMAT_RGB888
}
PixelFormat::Rgb565 | PixelFormat::Bgr565 => {
lcd_color_rgb_pixel_format_t_LCD_COLOR_PIXEL_FORMAT_RGB565
}
PixelFormat::Rgb666 | PixelFormat::Bgr666 => {
lcd_color_rgb_pixel_format_t_LCD_COLOR_PIXEL_FORMAT_RGB666
}
}
}
pub fn to_color_format(&self) -> lcd_color_format_t {
match self {
PixelFormat::Rgb888 | PixelFormat::Bgr888 => {
lcd_color_format_t_LCD_COLOR_FMT_RGB888
}
PixelFormat::Rgb565 | PixelFormat::Bgr565 => {
lcd_color_format_t_LCD_COLOR_FMT_RGB565
}
PixelFormat::Rgb666 | PixelFormat::Bgr666 => {
lcd_color_format_t_LCD_COLOR_FMT_RGB666
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RgbElementOrder {
Rgb,
Bgr,
}
impl From<RgbElementOrder> for lcd_rgb_element_order_t {
fn from(order: RgbElementOrder) -> Self {
match order {
RgbElementOrder::Rgb => lcd_rgb_element_order_t_LCD_RGB_ELEMENT_ORDER_RGB,
RgbElementOrder::Bgr => lcd_rgb_element_order_t_LCD_RGB_ELEMENT_ORDER_BGR,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RgbDataEndian {
BigEndian,
LittleEndian,
}
impl From<RgbDataEndian> for lcd_rgb_data_endian_t {
fn from(endian: RgbDataEndian) -> Self {
match endian {
RgbDataEndian::BigEndian => lcd_rgb_data_endian_t_LCD_RGB_DATA_ENDIAN_BIG,
RgbDataEndian::LittleEndian => lcd_rgb_data_endian_t_LCD_RGB_DATA_ENDIAN_LITTLE,
}
}
}
}
pub use config::*;
crate::impl_peripheral!(DSI);
pub trait DpiPanelState {}
pub struct NoDpiPanel;
impl DpiPanelState for NoDpiPanel {}
pub struct WithDpiPanel;
impl DpiPanelState for WithDpiPanel {}
struct DpiPanel {
handle: NonNull<c_void>,
config_box: Option<*mut esp_lcd_dpi_panel_config_t>,
draw_done_sem: SemaphoreHandle_t,
}
const QUEUE_TYPE_BINARY_SEMAPHORE: u8 = 3;
fn create_binary_semaphore() -> Result<SemaphoreHandle_t, EspError> {
let sem = unsafe { xQueueGenericCreate(1, 0, QUEUE_TYPE_BINARY_SEMAPHORE) };
if sem.is_null() {
Err(EspError::from_infallible::<ESP_ERR_NO_MEM>())
} else {
Ok(sem)
}
}
fn register_draw_done_cb(
panel_handle: PanelHandle,
sem: SemaphoreHandle_t,
) -> Result<(), EspError> {
unsafe {
let cbs = esp_lcd_dpi_panel_event_callbacks_t {
on_color_trans_done: Some(on_color_trans_done_isr),
..core::mem::zeroed()
};
esp!(esp_lcd_dpi_panel_register_event_callbacks(
panel_handle,
&cbs,
sem as *mut c_void,
))
}
}
unsafe extern "C" fn on_color_trans_done_isr(
_panel: esp_lcd_panel_handle_t,
_edata: *mut esp_lcd_dpi_panel_event_data_t,
user_ctx: *mut c_void,
) -> bool {
let sem = user_ctx as SemaphoreHandle_t;
let mut higher_prio_woken: BaseType_t = 0;
xQueueGiveFromISR(sem, &mut higher_prio_woken);
higher_prio_woken != 0
}
pub trait VendorPanel {
fn create(
&self,
io_handle: PanelIoHandle,
dev_config: &esp_lcd_panel_dev_config_t,
) -> Result<PanelHandle, EspError>;
}
pub struct LcdDriver<'d, S: DpiPanelState = NoDpiPanel> {
bus_handle: NonNull<c_void>,
dbi_io_handle: NonNull<c_void>,
control_panel_handle: Option<PanelHandle>,
dpi_panel: Option<DpiPanel>,
config: LcdConfig,
_p: PhantomData<&'d mut ()>,
_state: PhantomData<S>,
}
impl<'d, S: DpiPanelState> LcdDriver<'d, S> {
pub fn bus_handle(&self) -> DsiBusHandle {
self.bus_handle.as_ptr().cast()
}
pub fn dbi_io_handle(&self) -> PanelIoHandle {
self.dbi_io_handle.as_ptr().cast()
}
pub fn config(&self) -> &LcdConfig {
&self.config
}
pub fn control_panel_handle(&self) -> Option<PanelHandle> {
self.control_panel_handle
}
pub fn set_control_panel(
&mut self,
control_panel: PanelHandle,
) -> Result<Option<PanelHandle>, EspError> {
if control_panel.is_null() {
return Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>());
}
Ok(self.control_panel_handle.replace(control_panel))
}
pub fn with_vendor_panel<V: VendorPanel>(&mut self, vendor: &V) -> Result<(), EspError> {
let dev_config: esp_lcd_panel_dev_config_t = (&self.config).into();
self.with_vendor_panel_config(vendor, &dev_config)
}
pub fn with_vendor_panel_config<V: VendorPanel>(
&mut self,
vendor: &V,
dev_config: &esp_lcd_panel_dev_config_t,
) -> Result<(), EspError> {
let handle = vendor.create(self.dbi_io_handle(), dev_config)?;
self.set_control_panel(handle)?;
Ok(())
}
pub fn tx_param(&self, lcd_cmd: i32, param: &[u8]) -> Result<(), EspError> {
unsafe {
let param_ptr = if param.is_empty() {
core::ptr::null()
} else {
param.as_ptr() as *const c_void
};
esp!(esp_lcd_panel_io_tx_param(
self.dbi_io_handle(),
lcd_cmd,
param_ptr,
param.len(),
))?;
}
Ok(())
}
pub fn rx_param(&self, lcd_cmd: i32, param: &mut [u8]) -> Result<(), EspError> {
unsafe {
esp!(esp_lcd_panel_io_rx_param(
self.dbi_io_handle(),
lcd_cmd,
param.as_mut_ptr() as *mut c_void,
param.len(),
))?;
}
Ok(())
}
pub fn tx_color(&self, lcd_cmd: i32, color: &[u8]) -> Result<(), EspError> {
unsafe {
esp!(esp_lcd_panel_io_tx_color(
self.dbi_io_handle(),
lcd_cmd,
color.as_ptr() as *const c_void,
color.len(),
))?;
}
Ok(())
}
fn require_control_panel(&self) -> Result<PanelHandle, EspError> {
self.control_panel_handle
.ok_or(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())
}
pub fn reset(&self) -> Result<(), EspError> {
let panel = self.require_control_panel()?;
unsafe { esp!(esp_lcd_panel_reset(panel)) }
}
pub fn init(&self) -> Result<(), EspError> {
let panel = self.require_control_panel()?;
unsafe { esp!(esp_lcd_panel_init(panel)) }
}
pub fn set_display_on(&self, on: bool) -> Result<(), EspError> {
let panel = self.require_control_panel()?;
unsafe { esp!(esp_lcd_panel_disp_on_off(panel, on)) }
}
pub fn set_display_off(&self, off: bool) -> Result<(), EspError> {
let panel = self.require_control_panel()?;
unsafe { esp!(esp_lcd_panel_disp_off(panel, off)) }
}
pub fn set_display_sleep(&self, sleep: bool) -> Result<(), EspError> {
let panel = self.require_control_panel()?;
unsafe { esp!(esp_lcd_panel_disp_sleep(panel, sleep)) }
}
}
impl<'d> LcdDriver<'d, NoDpiPanel> {
pub fn new(_dsi: DSI<'d>, config: &LcdConfig) -> Result<Self, EspError> {
unsafe {
let mut bus_handle: DsiBusHandle = core::ptr::null_mut();
esp!(esp_lcd_new_dsi_bus(&config.bus_config, &mut bus_handle))?;
let bus_handle = NonNull::new(bus_handle as *mut c_void)
.ok_or(EspError::from_infallible::<ESP_ERR_INVALID_STATE>())?;
let mut dbi_io: PanelIoHandle = core::ptr::null_mut();
let result = esp!(esp_lcd_new_panel_io_dbi(
bus_handle.as_ptr().cast(),
&config.dbi_config,
&mut dbi_io,
));
if let Err(e) = result {
let _ = esp_lcd_del_dsi_bus(bus_handle.as_ptr().cast());
return Err(e);
}
let dbi_io_handle = match NonNull::new(dbi_io as *mut c_void) {
Some(h) => h,
None => {
let _ = esp_lcd_del_dsi_bus(bus_handle.as_ptr().cast());
return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
}
};
Ok(Self {
bus_handle,
dbi_io_handle,
control_panel_handle: None,
dpi_panel: None,
config: config.clone(),
_p: PhantomData,
_state: PhantomData,
})
}
}
pub fn create_dpi_panel(self) -> Result<LcdDriver<'d, WithDpiPanel>, EspError> {
unsafe {
let dpi_config = Box::new(self.config.dpi_config);
let dpi_config_ptr = Box::into_raw(dpi_config);
let mut panel_handle: PanelHandle = core::ptr::null_mut();
let result = esp!(esp_lcd_new_panel_dpi(
self.bus_handle.as_ptr().cast(),
dpi_config_ptr,
&mut panel_handle,
));
if let Err(e) = result {
let _ = Box::from_raw(dpi_config_ptr);
return Err(e);
}
let handle = match NonNull::new(panel_handle as *mut c_void) {
Some(h) => h,
None => {
let _ = Box::from_raw(dpi_config_ptr);
return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
}
};
let sem = create_binary_semaphore().inspect_err(|_| {
let _ = esp_lcd_panel_del(panel_handle);
let _ = Box::from_raw(dpi_config_ptr);
})?;
register_draw_done_cb(panel_handle, sem).inspect_err(|_| {
vQueueDelete(sem);
let _ = esp_lcd_panel_del(panel_handle);
let _ = Box::from_raw(dpi_config_ptr);
})?;
let this = ManuallyDrop::new(self);
Ok(LcdDriver {
bus_handle: this.bus_handle,
dbi_io_handle: this.dbi_io_handle,
control_panel_handle: this.control_panel_handle,
dpi_panel: Some(DpiPanel {
handle,
config_box: Some(dpi_config_ptr),
draw_done_sem: sem,
}),
config: core::ptr::read(&this.config),
_p: PhantomData,
_state: PhantomData,
})
}
}
pub fn set_dpi_panel(
self,
dpi_panel: PanelHandle,
) -> Result<LcdDriver<'d, WithDpiPanel>, EspError> {
let handle = NonNull::new(dpi_panel as *mut c_void)
.ok_or(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?;
let sem = create_binary_semaphore()?;
if let Err(e) = register_draw_done_cb(dpi_panel, sem) {
unsafe { vQueueDelete(sem) };
return Err(e);
}
let this = ManuallyDrop::new(self);
unsafe {
Ok(LcdDriver {
bus_handle: this.bus_handle,
dbi_io_handle: this.dbi_io_handle,
control_panel_handle: this.control_panel_handle,
dpi_panel: Some(DpiPanel {
handle,
config_box: None, draw_done_sem: sem,
}),
config: core::ptr::read(&this.config),
_p: PhantomData,
_state: PhantomData,
})
}
}
}
impl<'d> LcdDriver<'d, WithDpiPanel> {
pub fn panel_handle(&self) -> PanelHandle {
self.dpi_panel.as_ref().unwrap().handle.as_ptr().cast()
}
pub fn frame_buffer(&self) -> Result<*mut u8, EspError> {
let mut fb: *mut c_void = core::ptr::null_mut();
unsafe {
esp!(esp_lcd_dpi_panel_get_frame_buffer(
self.panel_handle(),
1,
&mut fb,
))?;
}
Ok(fb as *mut u8)
}
pub fn frame_buffers(&self) -> Result<(*mut u8, *mut u8), EspError> {
let mut fb0: *mut c_void = core::ptr::null_mut();
let mut fb1: *mut c_void = core::ptr::null_mut();
unsafe {
esp!(esp_lcd_dpi_panel_get_frame_buffer(
self.panel_handle(),
2,
&mut fb0,
&mut fb1,
))?;
}
Ok((fb0 as *mut u8, fb1 as *mut u8))
}
pub fn draw_bitmap(
&self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
data: &[u8],
) -> Result<(), EspError> {
unsafe {
esp!(esp_lcd_panel_draw_bitmap(
self.panel_handle(),
x1,
y1,
x2,
y2,
data.as_ptr() as *const c_void,
))
}
}
pub fn wait_for_draw(&self) {
let dpi = self.dpi_panel.as_ref().unwrap();
unsafe {
xQueueSemaphoreTake(dpi.draw_done_sem, u32::MAX);
}
}
pub fn invert_colors(&self, invert: bool) -> Result<(), EspError> {
unsafe { esp!(esp_lcd_panel_invert_color(self.panel_handle(), invert)) }
}
pub fn mirror_x(&self, mirror: bool) -> Result<(), EspError> {
unsafe { esp!(esp_lcd_panel_mirror(self.panel_handle(), mirror, false)) }
}
pub fn mirror_y(&self, mirror: bool) -> Result<(), EspError> {
unsafe { esp!(esp_lcd_panel_mirror(self.panel_handle(), false, mirror)) }
}
pub fn swap_xy(&self, swap: bool) -> Result<(), EspError> {
unsafe { esp!(esp_lcd_panel_swap_xy(self.panel_handle(), swap)) }
}
pub fn set_gap(&self, x_gap: i32, y_gap: i32) -> Result<(), EspError> {
unsafe { esp!(esp_lcd_panel_set_gap(self.panel_handle(), x_gap, y_gap)) }
}
#[cfg(feature = "embedded-graphics")]
pub fn display<C: eg::LcdColor>(&mut self) -> Result<eg::LcdDisplay<'_, 'd, C>, EspError> {
let num_fbs = self.config.dpi_config.num_fbs;
if num_fbs >= 2 {
let (fb0, fb1) = self.frame_buffers()?;
Ok(eg::LcdDisplay {
driver: self,
fb: fb1, fb_front: fb0, _color: PhantomData,
})
} else {
let fb = self.frame_buffer()?;
Ok(eg::LcdDisplay {
driver: self,
fb,
fb_front: core::ptr::null_mut(), _color: PhantomData,
})
}
}
}
impl<'d, S: DpiPanelState> Drop for LcdDriver<'d, S> {
fn drop(&mut self) {
unsafe {
if let Some(ref dpi) = self.dpi_panel {
let _ = esp_lcd_panel_del(dpi.handle.as_ptr().cast());
if let Some(config_ptr) = dpi.config_box {
if !config_ptr.is_null() {
let _ = Box::from_raw(config_ptr);
}
}
if !dpi.draw_done_sem.is_null() {
vQueueDelete(dpi.draw_done_sem);
}
}
if let Some(control) = self.control_panel_handle {
if !control.is_null() {
let _ = esp_lcd_panel_del(control);
}
}
let _ = esp_lcd_panel_io_del(self.dbi_io_handle.as_ptr().cast());
let _ = esp_lcd_del_dsi_bus(self.bus_handle.as_ptr().cast());
}
}
}
unsafe impl<'d, S: DpiPanelState> Send for LcdDriver<'d, S> {}
#[cfg(feature = "embedded-graphics")]
pub mod eg {
use super::*;
use embedded_graphics_core::draw_target::DrawTarget;
use embedded_graphics_core::geometry::{OriginDimensions, Size};
use embedded_graphics_core::pixelcolor::{
Bgr565, Bgr888, PixelColor, Rgb565, Rgb888, RgbColor,
};
use embedded_graphics_core::primitives::Rectangle;
use embedded_graphics_core::Pixel;
pub trait LcdColor: PixelColor + RgbColor {
fn pixel_format() -> PixelFormat;
fn bytes_per_pixel() -> usize;
fn write_to(&self, buf: &mut [u8], offset: usize);
}
impl LcdColor for Rgb888 {
fn pixel_format() -> PixelFormat {
PixelFormat::Rgb888
}
fn bytes_per_pixel() -> usize {
3
}
fn write_to(&self, buf: &mut [u8], offset: usize) {
buf[offset] = self.r();
buf[offset + 1] = self.g();
buf[offset + 2] = self.b();
}
}
impl LcdColor for Bgr888 {
fn pixel_format() -> PixelFormat {
PixelFormat::Bgr888
}
fn bytes_per_pixel() -> usize {
3
}
fn write_to(&self, buf: &mut [u8], offset: usize) {
buf[offset] = self.b();
buf[offset + 1] = self.g();
buf[offset + 2] = self.r();
}
}
impl LcdColor for Rgb565 {
fn pixel_format() -> PixelFormat {
PixelFormat::Rgb565
}
fn bytes_per_pixel() -> usize {
2
}
fn write_to(&self, buf: &mut [u8], offset: usize) {
let r = (self.r() >> 3) as u16;
let g = (self.g() >> 2) as u16;
let b = (self.b() >> 3) as u16;
let raw: u16 = (r << 11) | (g << 5) | b;
buf[offset..offset + 2].copy_from_slice(&raw.to_ne_bytes());
}
}
impl LcdColor for Bgr565 {
fn pixel_format() -> PixelFormat {
PixelFormat::Bgr565
}
fn bytes_per_pixel() -> usize {
2
}
fn write_to(&self, buf: &mut [u8], offset: usize) {
let b = (self.b() >> 3) as u16;
let g = (self.g() >> 2) as u16;
let r = (self.r() >> 3) as u16;
let raw: u16 = (b << 11) | (g << 5) | r;
buf[offset..offset + 2].copy_from_slice(&raw.to_ne_bytes());
}
}
pub struct LcdDisplay<'a, 'd, C: LcdColor> {
pub(crate) driver: &'a mut LcdDriver<'d, WithDpiPanel>,
pub(crate) fb: *mut u8,
pub(crate) fb_front: *mut u8,
pub(crate) _color: PhantomData<C>,
}
impl<C: LcdColor> OriginDimensions for LcdDisplay<'_, '_, C> {
fn size(&self) -> Size {
let timing = &self.driver.config().dpi_config().video_timing;
Size::new(timing.h_size, timing.v_size)
}
}
impl<C: LcdColor> LcdDisplay<'_, '_, C> {
fn fb_slice(&mut self) -> &mut [u8] {
let size = self.size();
let len = size.width as usize * size.height as usize * C::bytes_per_pixel();
unsafe { core::slice::from_raw_parts_mut(self.fb, len) }
}
fn fb_size(&self) -> usize {
let size = self.size();
size.width as usize * size.height as usize * C::bytes_per_pixel()
}
pub fn is_double_buffered(&self) -> bool {
!self.fb_front.is_null()
}
pub fn flush(&mut self) -> Result<(), EspError> {
if self.is_double_buffered() {
let size = self.size();
let fb_size = self.fb_size();
let back_buf = unsafe { core::slice::from_raw_parts(self.fb, fb_size) };
self.driver
.draw_bitmap(0, 0, size.width as i32, size.height as i32, back_buf)?;
self.driver.wait_for_draw();
core::mem::swap(&mut self.fb, &mut self.fb_front);
} else {
}
Ok(())
}
}
impl<C: LcdColor> DrawTarget for LcdDisplay<'_, '_, C> {
type Color = C;
type Error = EspError;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
let bpp = C::bytes_per_pixel();
let size = self.size();
let stride = size.width as usize * bpp;
let fb = self.fb_slice();
for Pixel(point, color) in pixels {
if point.x >= 0
&& point.y >= 0
&& (point.x as u32) < size.width
&& (point.y as u32) < size.height
{
let offset = point.y as usize * stride + point.x as usize * bpp;
C::write_to(&color, fb, offset);
}
}
Ok(())
}
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Self::Color>,
{
let bounding =
Rectangle::new(embedded_graphics_core::geometry::Point::zero(), self.size());
let area = area.intersection(&bounding);
let width = area.size.width as usize;
let height = area.size.height as usize;
if width == 0 || height == 0 {
return Ok(());
}
let bpp = C::bytes_per_pixel();
let stride = self.size().width as usize * bpp;
let x_start = area.top_left.x as usize;
let y_start = area.top_left.y as usize;
let fb = self.fb_slice();
let mut i = 0;
for color in colors.into_iter().take(width * height) {
let px = i % width;
let py = i / width;
let offset = (y_start + py) * stride + (x_start + px) * bpp;
C::write_to(&color, fb, offset);
i += 1;
}
Ok(())
}
fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
let bounding =
Rectangle::new(embedded_graphics_core::geometry::Point::zero(), self.size());
let area = area.intersection(&bounding);
let width = area.size.width as usize;
let height = area.size.height as usize;
if width == 0 || height == 0 {
return Ok(());
}
let bpp = C::bytes_per_pixel();
let stride = self.size().width as usize * bpp;
let x_start = area.top_left.x as usize;
let y_start = area.top_left.y as usize;
let row_bytes = width * bpp;
let mut row = alloc::vec![0u8; row_bytes];
for i in 0..width {
C::write_to(&color, &mut row, i * bpp);
}
let fb = self.fb_slice();
for y in 0..height {
let offset = (y_start + y) * stride + x_start * bpp;
fb[offset..offset + row_bytes].copy_from_slice(&row);
}
Ok(())
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
let size = self.size();
let rect = Rectangle::new(embedded_graphics_core::geometry::Point::zero(), size);
self.fill_solid(&rect, color)
}
}
}