#![no_std]
#![allow(clippy::type_complexity)]
pub mod instruction;
use crate::instruction::Instruction;
use display_interface::DataFormat;
use display_interface::WriteOnlyDataCommand;
pub mod error;
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::digital::v2::OutputPin;
pub use error::Error;
pub mod options;
use error::InitError;
pub use options::*;
pub mod builder;
pub use builder::Builder;
pub mod models;
use models::Model;
mod graphics;
#[cfg(feature = "batch")]
mod batch;
pub struct Display<DI, MODEL, RST>
where
DI: WriteOnlyDataCommand,
MODEL: Model,
RST: OutputPin,
{
di: DI,
model: MODEL,
rst: Option<RST>,
options: ModelOptions,
madctl: u8,
}
impl<DI, M, RST> Display<DI, M, RST>
where
DI: WriteOnlyDataCommand,
M: Model,
RST: OutputPin,
{
pub(crate) fn init(
&mut self,
delay_source: &mut impl DelayUs<u32>,
) -> Result<u8, InitError<RST::Error>> {
self.model
.init(&mut self.di, delay_source, &self.options, &mut self.rst)
}
pub fn orientation(&self) -> Orientation {
self.options.orientation()
}
pub fn set_orientation(&mut self, orientation: Orientation) -> Result<(), Error> {
let value = (self.madctl & 0b0001_1111) | orientation.value_u8();
self.write_command(Instruction::MADCTL)?;
self.write_data(&[value])?;
self.options.set_orientation(orientation);
self.madctl = value;
Ok(())
}
pub fn set_pixel(&mut self, x: u16, y: u16, color: M::ColorFormat) -> Result<(), Error> {
self.set_address_window(x, y, x, y)?;
self.model
.write_pixels(&mut self.di, core::iter::once(color))?;
Ok(())
}
pub fn set_pixels<T>(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
colors: T,
) -> Result<(), Error>
where
T: IntoIterator<Item = M::ColorFormat>,
{
self.set_address_window(sx, sy, ex, ey)?;
self.model.write_pixels(&mut self.di, colors)?;
Ok(())
}
pub fn set_scroll_region(&mut self, tfa: u16, vsa: u16, bfa: u16) -> Result<(), Error> {
self.write_command(Instruction::VSCRDER)?;
self.write_data(&tfa.to_be_bytes())?;
self.write_data(&vsa.to_be_bytes())?;
self.write_data(&bfa.to_be_bytes())?;
Ok(())
}
pub fn set_scroll_offset(&mut self, offset: u16) -> Result<(), Error> {
self.write_command(Instruction::VSCAD)?;
self.write_data(&offset.to_be_bytes())
}
pub fn release(self) -> (DI, M, Option<RST>) {
(self.di, self.model, self.rst)
}
fn write_command(&mut self, command: Instruction) -> Result<(), Error> {
self.di.send_commands(DataFormat::U8(&[command as u8]))
}
fn write_data(&mut self, data: &[u8]) -> Result<(), Error> {
self.di.send_data(DataFormat::U8(data))
}
fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), Error> {
let offset = self.options.window_offset();
let (sx, sy, ex, ey) = (sx + offset.0, sy + offset.1, ex + offset.0, ey + offset.1);
self.write_command(Instruction::CASET)?;
self.write_data(&sx.to_be_bytes())?;
self.write_data(&ex.to_be_bytes())?;
self.write_command(Instruction::RASET)?;
self.write_data(&sy.to_be_bytes())?;
self.write_data(&ey.to_be_bytes())
}
pub fn set_tearing_effect(&mut self, tearing_effect: TearingEffect) -> Result<(), Error> {
match tearing_effect {
TearingEffect::Off => self.write_command(Instruction::TEOFF),
TearingEffect::Vertical => {
self.write_command(Instruction::TEON)?;
self.write_data(&[0])
}
TearingEffect::HorizontalAndVertical => {
self.write_command(Instruction::TEON)?;
self.write_data(&[1])
}
}
}
}