#![no_std]
#![allow(clippy::type_complexity)]
pub mod instruction;
use crate::instruction::Instruction;
use num_derive::ToPrimitive;
use num_traits::ToPrimitive;
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::blocking::spi;
use embedded_hal::digital::v2::OutputPin;
#[cfg(feature = "graphics")]
mod graphics;
#[cfg(feature = "batch")]
mod batch;
pub struct ST7789<SPI, DC, RST, DELAY>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
{
spi: SPI,
dc: DC,
rst: RST,
size_x: u16,
size_y: u16,
delay: DELAY,
}
#[derive(ToPrimitive)]
pub enum Orientation {
Portrait = 0b0000_0000,
Landscape = 0b0110_0000,
PortraitSwapped = 0b1100_0000,
LandscapeSwapped = 0b1010_0000,
}
#[derive(Debug)]
pub enum Error<SPIE, DCE, RSTE> {
Spi(SPIE),
Dc(DCE),
Rst(RSTE),
}
impl<SPI, DC, RST, DELAY> ST7789<SPI, DC, RST, DELAY>
where
SPI: spi::Write<u8>,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
{
pub fn new(spi: SPI, dc: DC, rst: RST, size_x: u16, size_y: u16, delay: DELAY) -> Self {
ST7789 {
spi,
dc,
rst,
size_x,
size_y,
delay,
}
}
pub fn init(&mut self) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.hard_reset()?;
self.write_command(Instruction::SWRESET, None)?;
self.delay.delay_us(150_000);
self.write_command(Instruction::SLPOUT, None)?;
self.delay.delay_us(10_000);
self.write_command(Instruction::INVOFF, None)?;
self.write_command(Instruction::MADCTL, Some(&[0b0000_0000]))?;
self.write_command(Instruction::COLMOD, Some(&[0b0101_0101]))?;
self.write_command(Instruction::INVON, None)?;
self.delay.delay_us(10_000);
self.write_command(Instruction::NORON, None)?;
self.delay.delay_us(10_000);
self.write_command(Instruction::DISPON, None)?;
self.delay.delay_us(10_000);
Ok(())
}
pub fn hard_reset(&mut self) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.rst.set_high().map_err(Error::Rst)?;
self.delay.delay_us(10);
self.rst.set_low().map_err(Error::Rst)?;
self.delay.delay_us(10);
self.rst.set_high().map_err(Error::Rst)?;
self.delay.delay_us(10);
Ok(())
}
pub fn set_orientation(
&mut self,
orientation: &Orientation,
) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.write_command(Instruction::MADCTL, Some(&[orientation.to_u8().unwrap()]))?;
Ok(())
}
pub fn set_pixel(
&mut self,
x: u16,
y: u16,
color: u16,
) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.set_address_window(x, y, x, y)?;
self.write_command(Instruction::RAMWR, None)?;
self.start_data()?;
self.write_word(color)
}
pub fn set_pixels<T>(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
colors: T,
) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>>
where
T: IntoIterator<Item = u16>,
{
self.set_address_window(sx, sy, ex, ey)?;
self.write_command(Instruction::RAMWR, None)?;
self.start_data()?;
for color in colors {
self.write_word(color)?;
}
Ok(())
}
fn write_command(
&mut self,
command: Instruction,
params: Option<&[u8]>,
) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.dc.set_low().map_err(Error::Dc)?;
self.delay.delay_us(10);
self.spi
.write(&[command.to_u8().unwrap()])
.map_err(Error::Spi)?;
if let Some(params) = params {
self.start_data()?;
self.write_data(params)?;
}
Ok(())
}
fn start_data(&mut self) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.dc.set_high().map_err(Error::Dc)?;
self.delay.delay_us(10);
Ok(())
}
fn write_data(&mut self, data: &[u8]) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.spi.write(data).map_err(Error::Spi)
}
fn write_word(&mut self, value: u16) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.write_data(&value.to_be_bytes())
}
fn set_address_window(
&mut self,
sx: u16,
sy: u16,
ex: u16,
ey: u16,
) -> Result<(), Error<SPI::Error, DC::Error, RST::Error>> {
self.write_command(Instruction::CASET, None)?;
self.start_data()?;
self.write_word(sx)?;
self.write_word(ex)?;
self.write_command(Instruction::RASET, None)?;
self.start_data()?;
self.write_word(sy)?;
self.write_word(ey)
}
}