#![no_std]
#![allow(clippy::type_complexity)]
pub mod instruction;
use crate::instruction::Instruction;
use core::iter::once;
use display_interface::DataFormat::{U16BEIter, U8Iter};
use display_interface::WriteOnlyDataCommand;
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::digital::v2::OutputPin;
#[cfg(feature = "graphics")]
mod graphics;
#[cfg(feature = "batch")]
mod batch;
pub struct ST7789<DI, RST, BL>
where
DI: WriteOnlyDataCommand,
RST: OutputPin,
BL: OutputPin,
{
di: DI,
rst: Option<RST>,
bl: Option<BL>,
size_x: u16,
size_y: u16,
orientation: Orientation,
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum Orientation {
Portrait = 0b0000_0000, Landscape = 0b0110_0000, PortraitSwapped = 0b1100_0000, LandscapeSwapped = 0b1010_0000, }
impl Default for Orientation {
fn default() -> Self {
Self::Portrait
}
}
#[derive(Copy, Clone)]
pub enum TearingEffect {
Off,
Vertical,
HorizontalAndVertical,
}
#[derive(Copy, Clone, Debug)]
pub enum BacklightState {
On,
Off,
}
#[derive(Debug)]
pub enum Error<PinE> {
DisplayError,
Pin(PinE),
}
impl<DI, RST, BL, PinE> ST7789<DI, RST, BL>
where
DI: WriteOnlyDataCommand,
RST: OutputPin<Error = PinE>,
BL: OutputPin<Error = PinE>,
{
pub fn new(di: DI, rst: Option<RST>, bl: Option<BL>, size_x: u16, size_y: u16) -> Self {
Self {
di,
rst,
bl,
size_x,
size_y,
orientation: Orientation::default(),
}
}
pub fn init(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
self.hard_reset(delay_source)?;
if let Some(bl) = self.bl.as_mut() {
bl.set_low().map_err(Error::Pin)?;
delay_source.delay_us(10_000);
bl.set_high().map_err(Error::Pin)?;
}
self.write_command(Instruction::SWRESET)?; delay_source.delay_us(150_000);
self.write_command(Instruction::SLPOUT)?; delay_source.delay_us(10_000);
self.write_command(Instruction::INVOFF)?; self.write_command(Instruction::VSCRDER)?; self.write_data(&[0u8, 0u8, 0x14u8, 0u8, 0u8, 0u8])?; self.write_command(Instruction::MADCTL)?; self.write_data(&[0b0000_0000])?;
self.write_command(Instruction::COLMOD)?; self.write_data(&[0b0101_0101])?;
self.write_command(Instruction::INVON)?; delay_source.delay_us(10_000);
self.write_command(Instruction::NORON)?; delay_source.delay_us(10_000);
self.write_command(Instruction::DISPON)?; delay_source.delay_us(10_000);
Ok(())
}
pub fn hard_reset(&mut self, delay_source: &mut impl DelayUs<u32>) -> Result<(), Error<PinE>> {
if let Some(rst) = self.rst.as_mut() {
rst.set_high().map_err(Error::Pin)?;
delay_source.delay_us(10); rst.set_low().map_err(Error::Pin)?;
delay_source.delay_us(10); rst.set_high().map_err(Error::Pin)?;
delay_source.delay_us(10); }
Ok(())
}
pub fn set_backlight(
&mut self,
state: BacklightState,
delay_source: &mut impl DelayUs<u32>,
) -> Result<(), Error<PinE>> {
if let Some(bl) = self.bl.as_mut() {
match state {
BacklightState::On => bl.set_high().map_err(Error::Pin)?,
BacklightState::Off => bl.set_low().map_err(Error::Pin)?,
}
delay_source.delay_us(10); }
Ok(())
}
pub fn orientation(&self) -> Orientation {
self.orientation
}
pub fn set_orientation(&mut self, orientation: Orientation) -> Result<(), Error<PinE>> {
self.write_command(Instruction::MADCTL)?;
self.write_data(&[orientation as u8])?;
self.orientation = orientation;
Ok(())
}
pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) -> Result<(), Error<PinE>> {
self.set_address_window(x, y, x, y)?;
self.write_command(Instruction::RAMWR)?;
self.di
.send_data(U16BEIter(&mut once(color)))
.map_err(|_| Error::DisplayError)?;
Ok(())
}
pub fn set_pixels<T>(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
colors: T,
) -> Result<(), Error<PinE>>
where
T: IntoIterator<Item = u16>,
{
self.set_address_window(sx, sy, ex, ey)?;
self.write_command(Instruction::RAMWR)?;
self.di
.send_data(U16BEIter(&mut colors.into_iter()))
.map_err(|_| Error::DisplayError)
}
pub fn set_scroll_offset(&mut self, offset: u16) -> Result<(), Error<PinE>> {
self.write_command(Instruction::VSCAD)?;
self.write_data(&offset.to_be_bytes())
}
pub fn release(self) -> (DI, Option<RST>, Option<BL>) {
(self.di, self.rst, self.bl)
}
fn write_command(&mut self, command: Instruction) -> Result<(), Error<PinE>> {
self.di
.send_commands(U8Iter(&mut once(command as u8)))
.map_err(|_| Error::DisplayError)?;
Ok(())
}
fn write_data(&mut self, data: &[u8]) -> Result<(), Error<PinE>> {
self.di
.send_data(U8Iter(&mut data.iter().cloned()))
.map_err(|_| Error::DisplayError)
}
fn set_address_window(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
) -> Result<(), Error<PinE>> {
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<PinE>> {
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])
}
}
}
}