use core::marker::PhantomData;
use embedded_hal::{delay::DelayNs, digital::InputPin, digital::OutputPin, spi::SpiDevice};
use crate::DisplayBuffer;
enum Command {
Psr = 0x00,
PowerOff = 0x02,
PowerOn = 0x04,
BufferBlack = 0x10,
Refresh = 0x12,
BufferRed = 0x13,
ActiveTemperature = 0xe0,
InputTemperature = 0xe5,
}
const REG_DATA_SOFT_RESET: &[u8] = &[0x0e];
const REG_DATA_INPUT_TEMP: &[u8] = &[0x19];
const REG_DATA_ACTIVE_TEMP: &[u8] = &[0x02];
const REG_DATA_PSR: &[u8] = &[0xcf, 0x8d];
const TIMEOUT_MS: i32 = 60_000;
#[cfg(feature = "std")]
#[derive(thiserror::Error, Debug)]
pub enum Error<SpiError, DcError, RstError> {
#[error("SPI error: {0}")]
Spi(#[source] SpiError),
#[error("Error with GPIO 'DC': {0}")]
GpioDc(#[source] DcError),
#[error("Error with GPIO 'RESET': {0}")]
GpioRst(#[source] RstError),
#[error("Timeout while waiting for busy signal")]
Timeout,
}
#[cfg(not(feature = "std"))]
#[derive(Debug)]
pub enum Error<SpiError, DcError, RstError> {
Spi(SpiError),
GpioDc(DcError),
GpioRst(RstError),
Timeout,
}
type EpdError<SPI, DC, RST> = Error<
<SPI as embedded_hal::spi::ErrorType>::Error,
<DC as embedded_hal::digital::ErrorType>::Error,
<RST as embedded_hal::digital::ErrorType>::Error,
>;
type EpdResult<STATE, SPI, BUSY, DC, RST, DELAY> =
Result<Epd<STATE, SPI, BUSY, DC, RST, DELAY>, EpdError<SPI, DC, RST>>;
pub struct Epd<STATE: EpdState, SPI, BUSY, DC, RST, DELAY> {
busy: BUSY,
dc: DC,
rst: RST,
spi_chunk_size: usize,
spi: PhantomData<SPI>,
delay: PhantomData<DELAY>,
state: PhantomData<STATE>,
}
pub struct Active; pub struct Inactive; pub trait EpdState {}
impl EpdState for Active {}
impl EpdState for Inactive {}
impl<SPI, BUSY, DC, RST, DELAY> Epd<Inactive, SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
pub fn new(
_spi: &mut SPI,
busy: BUSY,
dc: DC,
rst: RST,
_delay: &mut DELAY,
spi_chunk_size: usize,
) -> Self {
Self {
busy,
dc,
rst,
spi_chunk_size,
spi: PhantomData,
delay: PhantomData,
state: PhantomData::<Inactive>,
}
}
pub fn init(
mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> EpdResult<Active, SPI, BUSY, DC, RST, DELAY> {
self.dc.set_high().map_err(Error::GpioDc)?;
self.reset(delay)?;
self.soft_reset(spi, delay)?;
self.send_data(spi, Command::InputTemperature, REG_DATA_INPUT_TEMP)?;
self.send_data(spi, Command::ActiveTemperature, REG_DATA_ACTIVE_TEMP)?;
self.send_data(spi, Command::Psr, REG_DATA_PSR)?;
Ok(Epd {
busy: self.busy,
dc: self.dc,
rst: self.rst,
spi_chunk_size: self.spi_chunk_size,
spi: PhantomData,
delay: PhantomData,
state: PhantomData::<Active>,
})
}
}
impl<SPI, BUSY, DC, RST, DELAY> Epd<Active, SPI, BUSY, DC, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
pub fn update(
&mut self,
display: &impl DisplayBuffer,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), EpdError<SPI, DC, RST>> {
self.send_data(spi, Command::BufferBlack, display.get_buffer_black())?;
self.send_data(spi, Command::BufferRed, display.get_buffer_red())?;
self.power_on(spi, delay)?;
self.display_refresh(spi, delay)?;
Ok(())
}
pub fn power_off(
mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> EpdResult<Inactive, SPI, BUSY, DC, RST, DELAY> {
self.send_data(spi, Command::PowerOff, &[0x0])?;
self.wait_busy(delay)?;
self.dc.set_low().map_err(Error::GpioDc)?;
delay.delay_ms(150);
self.rst.set_low().map_err(Error::GpioRst)?;
Ok(Epd {
busy: self.busy,
dc: self.dc,
rst: self.rst,
spi_chunk_size: self.spi_chunk_size,
spi: PhantomData,
delay: PhantomData,
state: PhantomData::<Inactive>,
})
}
}
impl<STATE, SPI, BUSY, DC, RST, DELAY> Epd<STATE, SPI, BUSY, DC, RST, DELAY>
where
STATE: EpdState,
SPI: SpiDevice,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn reset(&mut self, delay: &mut DELAY) -> Result<(), EpdError<SPI, DC, RST>> {
delay.delay_ms(1);
self.rst.set_high().map_err(Error::GpioRst)?;
delay.delay_ms(5);
self.rst.set_low().map_err(Error::GpioRst)?;
delay.delay_ms(10);
self.rst.set_high().map_err(Error::GpioRst)?;
delay.delay_ms(5);
Ok(())
}
fn power_on(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), EpdError<SPI, DC, RST>> {
self.send_data(spi, Command::PowerOn, &[0x0])?;
self.wait_busy(delay)?;
Ok(())
}
fn send_data(
&mut self,
spi: &mut SPI,
cmd: Command,
data: &[u8],
) -> Result<(), EpdError<SPI, DC, RST>> {
self.dc.set_low().map_err(Error::GpioDc)?;
self.write(spi, &[cmd as u8])?;
self.dc.set_high().map_err(Error::GpioDc)?;
self.write(spi, data)?;
Ok(())
}
fn write(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), EpdError<SPI, DC, RST>> {
if self.spi_chunk_size > 0 {
for chunk in data.chunks(self.spi_chunk_size) {
spi.write(chunk).map_err(Error::Spi)?;
}
} else {
spi.write(data).map_err(Error::Spi)?;
}
Ok(())
}
fn soft_reset(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), EpdError<SPI, DC, RST>> {
self.send_data(spi, Command::Psr, REG_DATA_SOFT_RESET)?;
self.wait_busy(delay)?;
Ok(())
}
fn display_refresh(
&mut self,
spi: &mut SPI,
delay: &mut DELAY,
) -> Result<(), EpdError<SPI, DC, RST>> {
self.send_data(spi, Command::Refresh, &[0x0])?;
self.wait_busy(delay)?;
Ok(())
}
fn wait_busy(&mut self, delay: &mut DELAY) -> Result<(), EpdError<SPI, DC, RST>> {
let delay_ms = 1;
let mut timeout = TIMEOUT_MS;
while self.busy.is_low().unwrap() && timeout > 0 {
delay.delay_ms(delay_ms);
timeout -= i32::try_from(delay_ms).unwrap();
}
if timeout <= 0 {
Err(Error::Timeout)
} else {
Ok(())
}
}
}
pub const SPI_MODE: embedded_hal::spi::Mode = embedded_hal::spi::Mode {
phase: embedded_hal::spi::Phase::CaptureOnFirstTransition,
polarity: embedded_hal::spi::Polarity::IdleLow,
};