#![no_std]
pub mod area;
pub mod bus;
pub mod color;
pub mod panel;
pub use crate::area::Area;
pub use crate::bus::{
BusBytesIo, BusHardwareFill, DisplayBus, FrameControl, Metadata, SimpleDisplayBus,
};
pub use color::{ColorFormat, ColorType, SolidColor};
pub use panel::{reset::LCDResetOption, Orientation, Panel, PanelSetBrightness};
use embedded_hal_async::delay::DelayNs;
#[derive(Debug)]
pub enum DisplayError<E> {
BusError(E),
Unsupported,
OutOfRange,
InvalidArgs,
UnalignedArea,
}
impl<E> From<E> for DisplayError<E> {
fn from(error: E) -> Self {
Self::BusError(error)
}
}
pub struct DisplayDriverBuilder<B: DisplayBus, P: Panel<B>> {
bus: B,
panel: P,
color_format: Option<ColorFormat>,
orientation: Option<Orientation>,
}
impl<B: DisplayBus, P: Panel<B>> DisplayDriverBuilder<B, P> {
fn new(bus: B, panel: P) -> Self {
Self {
bus,
panel,
color_format: None,
orientation: None,
}
}
pub fn with_color_format(mut self, color_format: ColorFormat) -> Self {
self.color_format = Some(color_format);
self
}
pub fn with_orientation(mut self, orientation: Orientation) -> Self {
self.orientation = Some(orientation);
self
}
pub async fn init<D: DelayNs>(
mut self,
delay: &mut D,
) -> Result<DisplayDriver<B, P>, DisplayError<B::Error>> {
self.panel
.init(&mut self.bus, delay)
.await
.map_err(DisplayError::BusError)?;
if let Some(color_format) = self.color_format {
self.panel
.set_color_format(&mut self.bus, color_format)
.await?;
}
if let Some(orientation) = self.orientation {
self.panel
.set_orientation(&mut self.bus, orientation)
.await?;
}
Ok(DisplayDriver {
bus: self.bus,
panel: self.panel,
})
}
}
pub struct DisplayDriver<B: DisplayBus, P: Panel<B>> {
pub bus: B,
pub panel: P,
}
impl<B: DisplayBus, P: Panel<B>> DisplayDriver<B, P> {
pub fn builder(bus: B, panel: P) -> DisplayDriverBuilder<B, P> {
DisplayDriverBuilder::new(bus, panel)
}
pub fn new(bus: B, panel: P) -> Self {
Self { bus, panel }
}
pub async fn init(&mut self, delay: &mut impl DelayNs) -> Result<(), DisplayError<B::Error>> {
self.panel
.init(&mut self.bus, delay)
.await
.map_err(DisplayError::BusError)
}
pub async fn set_window(&mut self, area: Area) -> Result<(), DisplayError<B::Error>> {
if self.panel.x_alignment() > 1 || self.panel.y_alignment() > 1 {
if area.x % self.panel.x_alignment() != 0
|| area.y % self.panel.y_alignment() != 0
|| area.w % self.panel.x_alignment() != 0
|| area.h % self.panel.y_alignment() != 0
{
return Err(DisplayError::UnalignedArea);
}
}
let (x1, y1) = area.bottom_right();
self.panel
.set_window(&mut self.bus, area.x, area.y, x1, y1)
.await
}
pub async fn set_color_format(
&mut self,
color_format: ColorFormat,
) -> Result<(), DisplayError<B::Error>> {
self.panel
.set_color_format(&mut self.bus, color_format)
.await
}
pub async fn set_orientation(
&mut self,
orientation: Orientation,
) -> Result<(), DisplayError<B::Error>> {
self.panel.set_orientation(&mut self.bus, orientation).await
}
pub async fn write_pixels(
&mut self,
area: Area,
frame_control: FrameControl,
buffer: &[u8],
) -> Result<(), DisplayError<B::Error>> {
self.set_window(area).await?;
let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
let metadata = Metadata {
area: Some(area),
frame_control,
};
self.bus.write_pixels(cmd, buffer, metadata).await
}
pub async fn write_frame(&mut self, buffer: &[u8]) -> Result<(), DisplayError<B::Error>> {
self.write_pixels(
Area::from_origin_size(self.panel.size()),
FrameControl::new_standalone(),
buffer,
)
.await
}
}
impl<B: DisplayBus, P: Panel<B> + PanelSetBrightness<B>> DisplayDriver<B, P> {
pub async fn set_brightness(&mut self, brightness: u8) -> Result<(), DisplayError<B::Error>> {
self.panel.set_brightness(&mut self.bus, brightness).await
}
}
impl<B: DisplayBus + BusHardwareFill, P: Panel<B>> DisplayDriver<B, P> {
pub async fn fill_solid_via_bus(
&mut self,
color: SolidColor,
area: Area,
) -> Result<(), DisplayError<B::Error>> {
self.set_window(area).await?;
let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
self.bus.fill_solid(cmd, color, area).await
}
pub async fn fill_screen_via_bus(
&mut self,
color: SolidColor,
) -> Result<(), DisplayError<B::Error>> {
self.fill_solid_via_bus(color, Area::from_origin_size(self.panel.size()))
.await
}
}
impl<B, P> DisplayDriver<B, P>
where
B: DisplayBus + BusBytesIo,
P: Panel<B>,
{
pub async fn fill_solid_batch<const N: usize>(
&mut self,
color: SolidColor,
area: Area,
) -> Result<(), DisplayError<B::Error>> {
self.set_window(area).await?;
let cmd = &P::PIXEL_WRITE_CMD[0..P::CMD_LEN];
self.bus
.write_cmd_bytes(cmd)
.await
.map_err(DisplayError::BusError)?;
let pixel_size = color.format.size_bytes() as usize;
let total_pixels = area.total_pixels();
let mut remaining_pixels = total_pixels;
let mut buffer = [0u8; N];
let pixels_per_chunk = buffer.len() / pixel_size;
let color_bytes = &color.raw[..pixel_size];
for i in 0..pixels_per_chunk {
buffer[i * pixel_size..(i + 1) * pixel_size].copy_from_slice(color_bytes);
}
while remaining_pixels > 0 {
let current_pixels = remaining_pixels.min(pixels_per_chunk);
let byte_count = current_pixels * pixel_size;
self.bus
.write_data_bytes(&buffer[0..byte_count])
.await
.map_err(DisplayError::BusError)?;
remaining_pixels -= current_pixels;
}
Ok(())
}
pub async fn fill_screen_batch<const N: usize>(
&mut self,
color: SolidColor,
) -> Result<(), DisplayError<B::Error>> {
self.fill_solid_batch::<N>(color, Area::from_origin_size(self.panel.size()))
.await
}
}