use core::slice::from_raw_parts;
use embedded_hal::{
delay::DelayNs,
digital::{InputPin, OutputPin},
spi::SpiDevice,
};
use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay};
mod constants;
use crate::epd2in9d::constants::*;
pub const WIDTH: u32 = 128;
pub const HEIGHT: u32 = 296;
pub const EPD_ARRAY: u32 = 4736;
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::Black;
const IS_BUSY_LOW: bool = false;
const SINGLE_BYTE_WRITE: bool = true;
use crate::color::Color;
pub(crate) mod command;
use self::command::Command;
use crate::buffer_len;
#[cfg(feature = "graphics")]
pub type Display2in9d = crate::graphics::Display<
WIDTH,
HEIGHT,
false,
{ buffer_len(WIDTH as usize, HEIGHT as usize) },
Color,
>;
pub struct Epd2in9d<'a, SPI, BUSY, DC, RST, DELAY> {
interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
color: Color,
refresh: RefreshLut,
old_data: &'a [u8],
is_partial_refresh: bool,
}
impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
for Epd2in9d<'_, 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.interface.reset(delay, 10_000, 2_000);
self.interface
.cmd_with_data(spi, Command::PanelSetting, &[0x1f, 0x0D])?;
self.interface
.cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?;
self.interface.cmd(spi, Command::PowerOn)?;
self.wait_until_idle(spi, delay)?;
self.interface
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?;
Ok(())
}
}
impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
for Epd2in9d<'_, SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
type DisplayColor = Color;
fn new(
spi: &mut SPI,
busy: BUSY,
dc: DC,
rst: RST,
delay: &mut DELAY,
delay_us: Option<u32>,
) -> Result<Self, SPI::Error> {
let interface = DisplayInterface::new(busy, dc, rst, delay_us);
let color = DEFAULT_BACKGROUND_COLOR;
let old_data: &[u8] = &[];
let is_partial_refresh = false;
let mut epd = Epd2in9d {
interface,
color,
refresh: RefreshLut::Full,
old_data,
is_partial_refresh,
};
epd.init(spi, delay)?;
Ok(epd)
}
fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.is_partial_refresh = false;
self.interface
.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0xf7])?;
self.interface.cmd(spi, Command::PowerOff)?;
self.wait_until_idle(spi, delay)?;
delay.delay_us(100_000);
self.interface
.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
Ok(())
}
fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.init(spi, delay)?;
Ok(())
}
fn set_background_color(&mut self, background_color: Color) {
self.color = background_color;
}
fn background_color(&self) -> &Color {
&self.color
}
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> {
if self.is_partial_refresh {
self.is_partial_refresh = false;
}
self.wait_until_idle(spi, delay)?;
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, 0xFF, EPD_ARRAY)?;
self.interface
.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?;
self.old_data = unsafe { from_raw_parts(buffer.as_ptr(), buffer.len()) };
Ok(())
}
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> {
if !self.is_partial_refresh {
self.set_part_reg(spi, delay)?;
self.is_partial_refresh = true;
}
self.interface.cmd(spi, Command::PartialIn)?;
self.interface.cmd(spi, Command::PartialWindow)?;
self.interface.data(spi, &[(x - x % 8) as u8])?;
self.interface
.data(spi, &[(((x - x % 8) + width - 1) - 1) as u8])?;
self.interface.data(spi, &[(y / 256) as u8])?;
self.interface.data(spi, &[(y % 256) as u8])?;
self.interface
.data(spi, &[((y + height - 1) / 256) as u8])?;
self.interface
.data(spi, &[((y + height - 1) % 256 - 1) as u8])?;
self.interface.data(spi, &[0x28])?;
self.interface
.cmd_with_data(spi, Command::DataStartTransmission1, self.old_data)?;
self.interface
.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?;
self.old_data = unsafe { from_raw_parts(buffer.as_ptr(), buffer.len()) };
Ok(())
}
fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.cmd(spi, Command::DisplayRefresh)?;
delay.delay_us(1_000);
self.wait_until_idle(spi, delay)?;
Ok(())
}
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)?;
Ok(())
}
fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.cmd(spi, Command::DataStartTransmission1)?;
self.interface.data_x_times(spi, 0x00, EPD_ARRAY)?;
self.interface.cmd(spi, Command::DataStartTransmission2)?;
self.interface.data_x_times(spi, 0xFF, EPD_ARRAY)?;
self.display_frame(spi, delay)?;
Ok(())
}
fn set_lut(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
refresh_rate: Option<RefreshLut>,
) -> Result<(), SPI::Error> {
if let Some(refresh_lut) = refresh_rate {
self.refresh = refresh_lut;
}
self.set_lut_helper(
spi, delay, &LUT_VCOM1, &LUT_WW1, &LUT_BW1, &LUT_WB1, &LUT_BB1,
)?;
Ok(())
}
fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.wait_until_idle(delay, IS_BUSY_LOW);
Ok(())
}
}
impl<SPI, BUSY, DC, RST, DELAY> Epd2in9d<'_, SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn set_part_reg(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
self.interface.reset(delay, 10_000, 2_000);
self.interface.cmd_with_data(
spi,
Command::PowerSetting,
&[0x03, 0x00, 0x2b, 0x2b, 0x03],
)?;
self.interface
.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
self.interface
.cmd_with_data(spi, Command::PanelSetting, &[0xbf, 0x0D])?;
self.interface
.cmd_with_data(spi, Command::PllControl, &[0x3C])?;
self.interface
.cmd_with_data(spi, Command::ResolutionSetting, &[0x80, 0x01, 0x28])?;
self.interface
.cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?;
self.set_lut(spi, delay, None)?;
self.interface.cmd(spi, Command::PowerOn)?;
self.wait_until_idle(spi, delay)?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn set_lut_helper(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
lut_vcom: &[u8],
lut_ww: &[u8],
lut_bw: &[u8],
lut_wb: &[u8],
lut_bb: &[u8],
) -> Result<(), SPI::Error> {
let _ = delay;
self.interface
.cmd_with_data(spi, Command::LutForVcom, lut_vcom)?;
self.interface
.cmd_with_data(spi, Command::LutWhiteToWhite, lut_ww)?;
self.interface
.cmd_with_data(spi, Command::LutBlackToWhite, lut_bw)?;
self.interface
.cmd_with_data(spi, Command::LutWhiteToBlack, lut_wb)?;
self.interface
.cmd_with_data(spi, Command::LutBlackToBlack, lut_bb)?;
Ok(())
}
}