use embedded_hal::{
delay::DelayNs,
digital::{InputPin, OutputPin},
spi::SpiDevice,
};
use crate::color::TriColor;
use crate::interface::DisplayInterface;
use crate::traits::{
InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
};
pub(crate) mod command;
use self::command::*;
use crate::buffer_len;
pub const WIDTH: u32 = 152;
pub const HEIGHT: u32 = 296;
const SINGLE_BYTE_WRITE: bool = true;
pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White;
#[cfg(feature = "graphics")]
pub type Display2in66b = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ buffer_len(WIDTH as usize, HEIGHT as usize) * 2 },
TriColor,
>;
pub struct Epd2in66b<SPI, BUSY, DC, RST, DELAY> {
interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
background: TriColor,
}
impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
for Epd2in66b<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.hw_reset(delay)?;
self.sw_reset(spi, delay)?;
self.data_entry_mode(spi, DataEntryRow::XMinor, DataEntrySign::IncYIncX)?;
self.set_display_window(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
self.update_control1(
spi,
WriteMode::Normal,
WriteMode::Normal,
OutputSource::S8ToS167,
)?;
self.set_cursor(spi, 0, 0)?;
Ok(())
}
}
impl<SPI, BUSY, DC, RST, DELAY> WaveshareThreeColorDisplay<SPI, BUSY, DC, RST, DELAY>
for Epd2in66b<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn update_color_frame(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
black: &[u8],
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.update_achromatic_frame(spi, delay, black)?;
self.update_chromatic_frame(spi, delay, chromatic)
}
fn update_achromatic_frame(
&mut self,
spi: &mut SPI,
_delay: &mut DELAY,
black: &[u8],
) -> Result<(), SPI::Error> {
self.set_cursor(spi, 0, 0)?;
self.interface.cmd(spi, Command::WriteBlackWhiteRAM)?;
self.interface.data(spi, black)
}
fn update_chromatic_frame(
&mut self,
spi: &mut SPI,
_delay: &mut DELAY,
chromatic: &[u8],
) -> Result<(), SPI::Error> {
self.set_cursor(spi, 0, 0)?;
self.interface.cmd(spi, Command::WriteRedRAM)?;
self.interface.data(spi, chromatic)
}
}
impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
for Epd2in66b<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
type DisplayColor = TriColor;
fn new(
spi: &mut SPI,
busy: BUSY,
dc: DC,
rst: RST,
delay: &mut DELAY,
delay_us: Option<u32>,
) -> Result<Self, SPI::Error>
where
Self: Sized,
{
let mut epd = Self {
interface: DisplayInterface::new(busy, dc, rst, delay_us),
background: DEFAULT_BACKGROUND_COLOR,
};
epd.init(spi, delay)?;
Ok(epd)
}
fn sleep(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::DeepSleepMode,
&[DeepSleep::SleepLosingRAM as u8],
)
}
fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.init(spi, delay)
}
fn set_background_color(&mut self, color: Self::DisplayColor) {
self.background = color;
}
fn background_color(&self) -> &Self::DisplayColor {
&self.background
}
fn width(&self) -> u32 {
WIDTH
}
fn height(&self) -> u32 {
HEIGHT
}
fn update_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.set_cursor(spi, 0, 0)?;
self.update_achromatic_frame(spi, delay, buffer)?;
self.red_pattern(spi, delay, PatW::W160, PatH::H296, StartWith::Zero) }
fn update_partial_frame(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.set_display_window(spi, x, y, x + width, y + height)?;
self.set_cursor(spi, x, y)?;
self.update_achromatic_frame(spi, delay, buffer)?;
self.set_display_window(spi, 0, 0, WIDTH, HEIGHT)
}
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.cmd(spi, Command::MasterActivation)?;
self.wait_until_idle(delay)
}
fn update_and_display_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
delay: &mut DELAY,
) -> Result<(), SPI::Error> {
self.update_frame(spi, buffer, delay)?;
self.display_frame(spi, delay)
}
fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
let (white, red) = match self.background {
TriColor::Black => (StartWith::Zero, StartWith::Zero),
TriColor::White => (StartWith::One, StartWith::Zero),
TriColor::Chromatic => (StartWith::Zero, StartWith::One),
};
self.black_white_pattern(spi, delay, PatW::W160, PatH::H296, white)?;
self.red_pattern(spi, delay, PatW::W160, PatH::H296, red)
}
fn set_lut(
&mut self,
_spi: &mut SPI,
_delay: &mut DELAY,
_refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
Ok(())
}
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.wait_until_idle(delay)
}
}
impl<SPI, BUSY, DC, RST, DELAY> Epd2in66b<SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn wait_until_idle(&mut self, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.wait_until_idle(delay, false);
Ok(())
}
fn hw_reset(&mut self, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.reset(delay, 20_000, 2_000);
self.wait_until_idle(delay)
}
fn sw_reset(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.cmd(spi, Command::Reset)?;
self.wait_until_idle(delay)
}
fn data_entry_mode(
&mut self,
spi: &mut SPI,
row: DataEntryRow,
sign: DataEntrySign,
) -> Result<(), SPI::Error> {
self.interface
.cmd_with_data(spi, Command::DataEntryMode, &[row as u8 | sign as u8])
}
fn set_display_window(
&mut self,
spi: &mut SPI,
xstart: u32,
ystart: u32,
xend: u32,
yend: u32,
) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::SetXAddressRange,
&[(((xstart >> 3) & 0x1f) as u8), (((xend >> 3) & 0x1f) as u8)],
)?;
self.interface.cmd_with_data(
spi,
Command::SetYAddressRange,
&[
((ystart & 0xff) as u8),
(((ystart >> 8) & 0x01) as u8),
((yend & 0xff) as u8),
(((yend >> 8) & 0x01) as u8),
],
)
}
fn update_control1(
&mut self,
spi: &mut SPI,
red_mode: WriteMode,
bw_mode: WriteMode,
source: OutputSource,
) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::DisplayUpdateControl1,
&[((red_mode as u8) << 4 | bw_mode as u8), (source as u8)],
)
}
fn set_cursor(&mut self, spi: &mut SPI, x: u32, y: u32) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::SetXAddressCounter,
&[((x >> 3) & 0x1f) as u8],
)?;
self.interface.cmd_with_data(
spi,
Command::SetYAddressCounter,
&[((y & 0xff) as u8), (((y >> 8) & 0x01) as u8)],
)
}
fn black_white_pattern(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
w: PatW,
h: PatH,
phase: StartWith,
) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::BlackWhiteRAMTestPattern,
&[phase as u8 | h as u8 | w as u8],
)?;
self.wait_until_idle(delay)
}
fn red_pattern(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
w: PatW,
h: PatH,
phase: StartWith,
) -> Result<(), SPI::Error> {
self.interface.cmd_with_data(
spi,
Command::RedRAMTestPattern,
&[phase as u8 | h as u8 | w as u8],
)?;
self.wait_until_idle(delay)
}
}