use crate::traits::Command; use core::marker::PhantomData; use embedded_hal::{ blocking::{delay::*, spi::Write}, digital::v2::*, }; /// The Connection Interface of all (?) Waveshare EPD-Devices /// pub(crate) struct DisplayInterface { /// SPI _spi: PhantomData, /// DELAY _delay: PhantomData, /// CS for SPI cs: CS, /// Low for busy, Wait until display is ready! busy: BUSY, /// Data/Command Control Pin (High for data, Low for command) dc: DC, /// Pin for Resetting rst: RST, } impl DisplayInterface where SPI: Write, CS: OutputPin, BUSY: InputPin, DC: OutputPin, RST: OutputPin, DELAY: DelayMs, { pub fn new(cs: CS, busy: BUSY, dc: DC, rst: RST) -> Self { DisplayInterface { _spi: PhantomData::default(), _delay: PhantomData::default(), cs, busy, dc, rst, } } /// Basic function for sending [Commands](Command). /// /// Enables direct interaction with the device with the help of [data()](DisplayInterface::data()) pub(crate) fn cmd(&mut self, spi: &mut SPI, command: T) -> Result<(), SPI::Error> { // low for commands let _ = self.dc.set_low(); // Transfer the command over spi self.write(spi, &[command.address()]) } /// Basic function for sending an array of u8-values of data over spi /// /// Enables direct interaction with the device with the help of [command()](Epd4in2::command()) pub(crate) fn data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { // high for data let _ = self.dc.set_high(); for val in data.iter().copied() { // Transfer data one u8 at a time over spi self.write(spi, &[val])?; } Ok(()) } /// Basic function for sending [Commands](Command) and the data belonging to it. /// /// TODO: directly use ::write? cs wouldn't needed to be changed twice than pub(crate) fn cmd_with_data( &mut self, spi: &mut SPI, command: T, data: &[u8], ) -> Result<(), SPI::Error> { self.cmd(spi, command)?; self.data(spi, data) } /// Basic function for sending the same byte of data (one u8) multiple times over spi /// /// Enables direct interaction with the device with the help of [command()](ConnectionInterface::command()) pub(crate) fn data_x_times( &mut self, spi: &mut SPI, val: u8, repetitions: u32, ) -> Result<(), SPI::Error> { // high for data let _ = self.dc.set_high(); // Transfer data (u8) over spi for _ in 0..repetitions { self.write(spi, &[val])?; } Ok(()) } // spi write helper/abstraction function fn write(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { // activate spi with cs low let _ = self.cs.set_low(); // transfer spi data // Be careful!! Linux has a default limit of 4096 bytes per spi transfer // see https://raspberrypi.stackexchange.com/questions/65595/spi-transfer-fails-with-buffer-size-greater-than-4096 if cfg!(target_os = "linux") { for data_chunk in data.chunks(4096) { spi.write(data_chunk)?; } } else { spi.write(data)?; } // deactivate spi with cs high let _ = self.cs.set_high(); Ok(()) } /// Waits until device isn't busy anymore (busy == HIGH) /// /// This is normally handled by the more complicated commands themselves, /// but in the case you send data and commands directly you might need to check /// if the device is still busy /// /// is_busy_low /// /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) /// /// Most likely there was a mistake with the 2in9 busy connection /// //TODO: use the #cfg feature to make this compile the right way for the certain types pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) { // //tested: worked without the delay for all tested devices // //self.delay_ms(1); while self.is_busy(is_busy_low) { // //tested: REMOVAL of DELAY: it's only waiting for the signal anyway and should continue work asap // //old: shorten the time? it was 100 in the beginning // //self.delay_ms(5); } } /// Checks if device is still busy /// /// This is normally handled by the more complicated commands themselves, /// but in the case you send data and commands directly you might need to check /// if the device is still busy /// /// is_busy_low /// /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) /// /// Most likely there was a mistake with the 2in9 busy connection /// //TODO: use the #cfg feature to make this compile the right way for the certain types pub(crate) fn is_busy(&self, is_busy_low: bool) -> bool { (is_busy_low && self.busy.is_low().unwrap_or(false)) || (!is_busy_low && self.busy.is_high().unwrap_or(false)) } /// Resets the device. /// /// Often used to awake the module from deep sleep. See [Epd4in2::sleep()](Epd4in2::sleep()) /// /// The timing of keeping the reset pin low seems to be important and different per device. /// Most displays seem to require keeping it low for 10ms, but the 7in5_v2 only seems to reset /// properly with 2ms pub(crate) fn reset(&mut self, delay: &mut DELAY, duration: u8) { let _ = self.rst.set_high(); delay.delay_ms(10); let _ = self.rst.set_low(); delay.delay_ms(duration); let _ = self.rst.set_high(); //TODO: the upstream libraries always sleep for 200ms here // 10ms works fine with just for the 7in5_v2 but this needs to be validated for other devices delay.delay_ms(200); } }