epd-datafuri 0.1.1

Driver for Adafruit e-Paper display (EPD) controllers (SSD1680, IL0373), for use with embedded-hal
Documentation
//! Display interface using SPI
use display_interface::DisplayError;
use embedded_hal::{
    delay::DelayNs,
    digital::{InputPin, OutputPin},
    spi::SpiDevice,
};

const RESET_DELAY_MS: u8 = 10;

/// SPI connection interface for EPD displays.
///
/// Busy pin polarity varies by controller: SSD1680 is active-high (HIGH = busy),
/// IL0373 is active-low (LOW = busy). Use `wait_until_idle` or
/// `wait_until_idle_active_low` accordingly.
pub struct SpiDisplayInterface<SPI, BSY, DC, RST> {
    /// SPI device
    spi: SPI,
    /// Busy status pin (polarity is controller-dependent)
    busy: BSY,
    /// Data/Command Control Pin (High for data, Low for command)
    dc: DC,
    /// Reset pin
    rst: RST,
}

impl<SPI, BSY, DC, RST> SpiDisplayInterface<SPI, BSY, DC, RST> {
    /// Create and initialize display
    pub fn new(spi: SPI, busy: BSY, dc: DC, rst: RST) -> Self {
        SpiDisplayInterface { spi, busy, dc, rst }
    }
}

impl<SPI, BSY, DC, RST> SpiDisplayInterface<SPI, BSY, DC, RST>
where
    SPI: SpiDevice,
    RST: OutputPin,
    DC: OutputPin,
    BSY: InputPin,
{
    /// Basic function for sending commands
    pub(crate) fn cmd(&mut self, command: u8) -> Result<(), DisplayError> {
        log::trace!("cmd 0x{:02X}", command);

        // low for commands
        self.dc.set_low().map_err(|_| DisplayError::DCError)?;

        // Transfer the command over spi
        self.spi
            .write(&[command])
            .map_err(|_| DisplayError::BusWriteError)
    }

    /// Basic function for sending an array of u8-values of data over spi
    pub(crate) fn data(&mut self, data: &[u8]) -> Result<(), DisplayError> {
        // high for data
        self.dc.set_high().map_err(|_| DisplayError::DCError)?;

        // Transfer data (u8-array) over spi
        self.spi
            .write(data)
            .map_err(|_| DisplayError::BusWriteError)
    }

    /// Basic function for sending a command and the data belonging to it.
    pub(crate) fn cmd_with_data(&mut self, command: u8, data: &[u8]) -> Result<(), DisplayError> {
        log::trace!("cmd 0x{:02X}, data {:02X?}", command, data);
        self.cmd(command)?;
        self.data(data)
    }

    /// Basic function for sending the same byte of data (one u8) multiple times over spi
    /// Useful for setting one color for the whole frame
    pub(crate) fn data_x_times(&mut self, val: u8, repetitions: u32) -> Result<(), DisplayError> {
        log::trace!("writing data 0x{:02X} x {}", val, repetitions);
        // high for data
        let _ = self.dc.set_high();
        // Transfer data (u8) over spi
        for _ in 0..repetitions {
            self.spi
                .write(&[val])
                .map_err(|_| DisplayError::BusWriteError)?;
        }
        Ok(())
    }

    /// Waits until device isn't busy anymore (busy == HIGH).
    ///
    /// Used by SSD1680-based displays where the busy pin is active-high (HIGH = busy).
    pub(crate) fn wait_until_idle(&mut self, delay: &mut impl DelayNs) {
        log::trace!("Waiting until display is idle");
        while self.busy.is_high().unwrap_or(true) {
            delay.delay_ms(1)
        }
    }

    /// Waits until device isn't busy anymore (busy == LOW).
    ///
    /// Used by IL0373-based displays where the busy pin is active-low (LOW = busy, HIGH = ready).
    pub(crate) fn wait_until_idle_active_low(&mut self, delay: &mut impl DelayNs) {
        log::trace!("Waiting until display is idle (active-low busy)");
        while self.busy.is_low().unwrap_or(true) {
            delay.delay_ms(1)
        }
    }

    /// Resets the device.
    pub(crate) fn hard_reset(&mut self, delay: &mut impl DelayNs) -> Result<(), DisplayError> {
        log::trace!("Resetting display");
        self.rst.set_low().map_err(|_| DisplayError::RSError)?;
        delay.delay_ms(RESET_DELAY_MS.into());
        self.rst.set_high().map_err(|_| DisplayError::RSError)?;
        delay.delay_ms(RESET_DELAY_MS.into());
        Ok(())
    }
}