diff --git a/examples/embedded_linux/src/main.rs b/examples/embedded_linux/src/main.rs index 388b535..ced4d9d 100644 --- a/examples/embedded_linux/src/main.rs +++ b/examples/embedded_linux/src/main.rs @@ -5,7 +5,7 @@ extern crate linux_embedded_hal as lin_hal; extern crate eink_waveshare_rs; -use eink_waveshare_rs::{epd4in2::EPD4in2, drawing::{Graphics, color::Color}}; +use eink_waveshare_rs::{epd4in2::EPD4in2, drawing::{Graphics, color::Color}, interface::WaveshareInterface}; use lin_hal::spidev::{self, SpidevOptions}; use lin_hal::{Pin, Spidev}; @@ -120,27 +120,27 @@ fn main() { graphics.draw_vertical_line(200, 50, 200, &Color::Black); - epd4in2.display_and_transfer_frame(graphics.get_buffer(), None).expect("display and transfer error"); + epd4in2.update_and_display_frame(graphics.get_buffer()).expect("display and transfer error"); epd4in2.delay_ms(3000); - epd4in2.clear_frame(None).expect("clear frame error"); + epd4in2.clear_frame().expect("clear frame error"); //Test fast updating a bit more let mut small_buffer = [0x00; 128]; let mut circle_graphics = Graphics::new(32,32, &mut small_buffer); circle_graphics.draw_circle(16,16, 10, &Color::Black); - epd4in2.set_partial_window(circle_graphics.get_buffer(), 16,16, 32, 32, false).expect("Partial Window Error"); + epd4in2.update_partial_frame(circle_graphics.get_buffer(), 16,16, 32, 32).expect("Partial Window Error"); epd4in2.display_frame().expect("Display Frame Error"); - epd4in2.set_partial_window(circle_graphics.get_buffer(), 128,64, 32, 32, false).expect("Partial Window Error"); + epd4in2.update_partial_frame(circle_graphics.get_buffer(), 128,64, 32, 32).expect("Partial Window Error"); epd4in2.display_frame().expect("Display Frame Error"); - epd4in2.set_partial_window(circle_graphics.get_buffer(), 320,24, 32, 32, false).expect("Partial Window Error"); + epd4in2.update_partial_frame(circle_graphics.get_buffer(), 320,24, 32, 32).expect("Partial Window Error"); epd4in2.display_frame().expect("Display Frame Error"); - epd4in2.set_partial_window(circle_graphics.get_buffer(), 160,240, 32, 32, false).expect("Partial Window Error"); + epd4in2.update_partial_frame(circle_graphics.get_buffer(), 160,240, 32, 32).expect("Partial Window Error"); epd4in2.display_frame().expect("Display Frame Error"); epd4in2.delay_ms(3000); @@ -152,7 +152,7 @@ fn main() { graphics.draw_string_8x8(16, 16, "hello", &Color::Black); graphics.draw_char_8x8(250, 250, '#', &Color::Black); graphics.draw_char_8x8(300, 16, '7', &Color::Black); - epd4in2.display_and_transfer_frame(graphics.get_buffer(), None).expect("display and transfer error"); + epd4in2.update_and_display_frame(graphics.get_buffer()).expect("display and transfer error"); epd4in2.delay_ms(3000); diff --git a/src/epd2in9/command.rs b/src/epd2in9/command.rs index 8eed1c7..b777e95 100644 --- a/src/epd2in9/command.rs +++ b/src/epd2in9/command.rs @@ -10,7 +10,7 @@ use interface; /// For more infos about the addresses and what they are doing look into the pdfs /// /// The description of the single commands is mostly taken from IL0398.pdf -#[allow(dead_code)] +//#[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub enum Command { @@ -38,6 +38,39 @@ pub enum Command { /// A[0] = 1: Enter Deep Sleep Mode DEEP_SLEEP_MODE = 0x10, // /// Data Entry mode setting + DATA_ENTRY_MODE_SETTING = 0x11, + + SW_RESET = 0x12, + + TEMPERATURE_SENSOR_CONTROL = 0x1A, + + MASTER_ACTIVATION = 0x20, + + DISPLAY_UPDATE_CONTROL_1 = 0x21, + + DISPLAY_UPDATE_CONTROL_2 = 0x22, + + WRITE_RAM = 0x24, + + WRITE_VCOM_REGISTER = 0x2C, + + WRITE_LUT_REGISTER = 0x32, + + SET_DUMMY_LINE_PERIOD = 0x3A, + + SET_GATE_TIME = 0x3B, + + BORDER_WAVEFORM_CONTROL = 0x3C, + + SET_RAM_X_ADDRESS_START_END_POSITION = 0x44, + + SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45, + + SET_RAM_X_ADDRESS_COUNTER = 0x4E, + + SET_RAM_Y_ADDRESS_COUNTER = 0x4F, + + TERMINATE_COMMANDS_AND_FRAME_WRITE = 0xFF } diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index ab8e35d..d1431d9 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -62,21 +62,23 @@ use self::constants::*; use drawing::color::Color; pub mod command; -pub use command::Command as Command; +pub use self::command::Command; use interface::*; -use interface::data_interface::DataInterface; +use interface::connection_interface::ConnectionInterface; /// EPD2in9 driver /// pub struct EPD2in9 { /// SPI - interface: DataInterface, + interface: ConnectionInterface, /// Width width: u32, /// Height - height: u32, + height: u32, + /// Color + color: Color, } impl EPD2in9 @@ -122,9 +124,11 @@ where let width = WIDTH as u32; let height = HEIGHT as u32; - let mut interface = DataInterface::new(spi, cs, busy, dc, rst, delay); + let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); - let mut epd = EPD2in9 {interface, width, height}; + let color = Color::White; + + let mut epd = EPD2in9 {interface, width, height, color}; epd.init()?; @@ -135,11 +139,26 @@ where fn init(&mut self) -> Result<(), E> { + self.reset(); + + + + unimplemented!() } + fn sleep(&mut self) -> Result<(), E> { - unimplemented!() + + self.interface.send_command(Command::DEEP_SLEEP_MODE)?; + // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode + //TODO: is 0x00 needed here? + self.interface.send_data(0x00)?; + + self.interface.wait_until_idle(false); + Ok(()) } + + fn reset(&mut self) { self.interface.reset() } @@ -165,6 +184,9 @@ where // TODO: add this abstraction function // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + unimplemented!() + } fn clear_frame(&mut self) -> Result<(), E>{ @@ -173,7 +195,7 @@ where /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) fn set_background_color(&mut self, color: Color){ - unimplemented!() + self.color = color; } } diff --git a/src/epd4in2/command.rs b/src/epd4in2/command.rs index c81bd79..a73e43e 100644 --- a/src/epd4in2/command.rs +++ b/src/epd4in2/command.rs @@ -1,5 +1,5 @@ //! SPI Commands for the Waveshare 4.2" E-Ink Display - +use interface; /// EPD4IN2 commands /// /// Should rarely (never?) be needed directly. @@ -146,9 +146,9 @@ pub enum Command { -impl Command { +impl interface::Command for Command { /// Returns the address of the command - pub fn addr(self) -> u8 { + fn address(self) -> u8 { self as u8 } } @@ -157,13 +157,14 @@ impl Command { #[cfg(test)] mod tests { use super::*; + use interface::Command as CommandTrait; #[test] fn command_addr() { - assert_eq!(Command::POWER_SAVING.addr(), 0xE3); + assert_eq!(Command::POWER_SAVING.address(), 0xE3); - assert_eq!(Command::PANEL_SETTING.addr(), 0x00); + assert_eq!(Command::PANEL_SETTING.address(), 0x00); - assert_eq!(Command::DISPLAY_REFRESH.addr(), 0x12); + assert_eq!(Command::DISPLAY_REFRESH.address(), 0x12); } } \ No newline at end of file diff --git a/src/epd4in2/lut.rs b/src/epd4in2/constants.rs similarity index 97% rename from src/epd4in2/lut.rs rename to src/epd4in2/constants.rs index 55c7d39..ea703e5 100644 --- a/src/epd4in2/lut.rs +++ b/src/epd4in2/constants.rs @@ -1,3 +1,6 @@ +pub(crate) const WIDTH: usize = 400; +pub(crate) const HEIGHT: usize = 300; + pub(crate) const LUT_VCOM0: [u8; 44] = [ 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, @@ -8,6 +11,7 @@ pub(crate) const LUT_VCOM0: [u8; 44] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_VCOM0_QUICK: [u8; 44] = [ 0x00, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -18,6 +22,7 @@ pub(crate) const LUT_VCOM0_QUICK: [u8; 44] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_WW: [u8; 42] =[ 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, @@ -28,6 +33,7 @@ pub(crate) const LUT_WW: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_WW_QUICK: [u8; 42] =[ 0xA0, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -49,6 +55,7 @@ pub(crate) const LUT_BW: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_BW_QUICK: [u8; 42] =[ 0xA0, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -70,6 +77,7 @@ pub(crate) const LUT_BB: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_BB_QUICK: [u8; 42] =[ 0x50, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -91,6 +99,7 @@ pub(crate) const LUT_WB: [u8; 42] =[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; + pub(crate) const LUT_WB_QUICK: [u8; 42] =[ 0x50, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 3f53d9a..c5a260b 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -1,27 +1,27 @@ //! A simple Driver for the Waveshare 4.2" E-Ink Display via SPI -//! +//! //! The other Waveshare E-Ink Displays should be added later on -//! -//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module), +//! +//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module), //! [Ben Krasnows partial Refresh tips](https://benkrasnow.blogspot.de/2017/10/fast-partial-refresh-on-42-e-paper.html) and //! the driver documents in the `pdfs`-folder as orientation. //! //! This driver was built using [`embedded-hal`] traits. //! //! [`embedded-hal`]: https://docs.rs/embedded-hal/~0.1 -//! +//! //! # Requirements -//! +//! //! ### SPI -//! +//! //! - MISO is not connected/available //! - SPI_MODE_0 is used (CPHL = 0, CPOL = 0) //! - 8 bits per word, MSB first //! - Max. Speed tested was 8Mhz but more should be possible -//! +//! //! ### Other.... -//! -//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, +//! +//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, //! where width and length being either the full e-ink size or the partial update window size //! //! # Examples @@ -30,82 +30,64 @@ //! let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay).unwrap(); //! //! let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; -//! +//! //! // draw something into the buffer -//! +//! //! epd4in2.display_and_transfer_buffer(buffer, None); -//! +//! //! // wait and look at the image -//! +//! //! epd4in2.clear_frame(None); -//! +//! //! epd4in2.sleep(); //! ``` -//! -//! +//! +//! //! //! BE CAREFUL! The Partial Drawing can "destroy" your display. //! It needs more testing first. - use hal::{ - blocking::{ - spi::Write, - delay::* - }, + blocking::{delay::*, spi::Write}, + digital::*, spi::{Mode, Phase, Polarity}, - digital::* }; +use interface::{connection_interface::ConnectionInterface, WaveshareInterface}; + //The Lookup Tables for the Display -mod lut; -use self::lut::*; +mod constants; +use self::constants::*; use drawing::color::Color; pub mod command; -pub use command::Command as Command; +pub use self::command::Command; //TODO: test spi mode -/// SPI mode - +/// SPI mode - /// For more infos see [Requirements: SPI](index.html#spi) pub const SPI_MODE: Mode = Mode { phase: Phase::CaptureOnFirstTransition, polarity: Polarity::IdleLow, }; - - - - - - - - /// EPD4in2 driver /// pub struct EPD4in2 { - /// SPI - spi: SPI, - /// 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 Reseting - rst: RST, - /// The concrete Delay implementation - delay: D, + /// Connection Interface + interface: ConnectionInterface, /// Width - width: u16, + width: u32, /// Height - height: u16, + height: u32, + /// Background Color + color: Color, } - -impl EPD4in2 -where +impl WaveshareInterface + for EPD4in2 +where SPI: Write, CS: OutputPin, BUSY: InputPin, @@ -113,54 +95,48 @@ where RST: OutputPin, D: DelayUs + DelayMs, { - /// Get the width of the display - pub fn get_width(&self) -> u16 { + fn get_width(&self) -> u32 { self.width } - /// Get the height of the display - pub fn get_height(&self) -> u16 { + fn get_height(&self) -> u32 { self.height } - - + /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC - /// + /// /// This already initialises the device. That means [EPD4in2::init()](EPD4in2::init()) isn't needed directly afterwards - /// + /// /// # Example - /// + /// /// ```ignore /// //buffer = some image data; - /// + /// /// let mut epd4in2 = EPD4in2::new(spi, cs, busy, dc, rst, delay); - /// + /// /// epd4in2.display_and_transfer_frame(buffer, None); - /// + /// /// epd4in2.sleep(); /// ``` - pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Result { - //TODO: width und height anpassbar machen? - let width = 400; - let height = 300; - - let mut epd4in2 = EPD4in2 {spi, cs, busy, dc, rst, delay, width, height }; - - epd4in2.init()?; - - Ok(epd4in2) + fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Result { + let width = WIDTH as u32; + let height = HEIGHT as u32; + + let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); + let color = Color::White; + let mut epd = EPD4in2 { + interface, + width, + height, + color, + }; + + epd.init()?; + + Ok(epd) } - - - /// This initialises the EPD and powers it up - /// - /// This function is already called from [EPD4in2::new()](EPD4in2::new()) - /// - /// This function calls [EPD4in2::reset()](EPD4in2::reset()), - /// so you don't need to call reset your self when trying to wake your device up - /// after setting it to sleep. - pub fn init(&mut self) -> Result<(), E> { + fn init(&mut self) -> Result<(), E> { // reset the device self.reset(); @@ -175,7 +151,7 @@ where // start the booster self.send_command(Command::BOOSTER_SOFT_START)?; for _ in 0..3 { - self.send_data(0x17)?; //07 0f 17 1f 27 2F 37 2f + self.send_data(0x17)?; //07 0f 17 1f 27 2F 37 2f } // power on @@ -198,68 +174,46 @@ where // Set Frequency, 200 Hz didn't work on my board // 150Hz and 171Hz wasn't tested yet // TODO: Test these other frequencies - // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ DEFAULT: 3c 50Hz + // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ DEFAULT: 3c 50Hz self.send_command(Command::PLL_CONTROL)?; self.send_data(0x3A)?; + self.set_lut()?; + Ok(()) } + fn sleep(&mut self) -> Result<(), E> { + self.send_command(Command::VCOM_AND_DATA_INTERVAL_SETTING)?; + self.send_data(0x17)?; //border floating + self.send_command(Command::VCM_DC_SETTING)?; // VCOM to 0V + self.send_command(Command::PANEL_SETTING)?; + self.delay_ms(100); - - - - - /// Transmit partial data to the SRAM of the EPD, - /// the final parameter dtm chooses between the 2 - /// internal buffers - /// - /// Normally it should be dtm2, so use false - /// - /// BUFFER needs to be of size: w / 8 * l ! - pub fn set_partial_window(&mut self, buffer: &[u8], x: u16, y: u16, w: u16, l: u16, is_dtm1: bool) -> Result<(), E> { - if buffer.len() as u16 != w / 8 * l { - //TODO: panic!! or sth like that - //return Err("Wrong buffersize"); + self.send_command(Command::POWER_SETTING)?; //VG&VS to 0V fast + for _ in 0..4 { + self.send_data(0x00)?; } + self.delay_ms(100); - self.send_command(Command::PARTIAL_IN)?; - self.send_command(Command::PARTIAL_WINDOW)?; - self.send_data((x >> 8) as u8)?; - let tmp = x & 0xf8; - self.send_data(tmp as u8)?; // x should be the multiple of 8, the last 3 bit will always be ignored - let tmp = tmp + w - 1; - self.send_data((tmp >> 8) as u8)?; - self.send_data((tmp | 0x07) as u8)?; - - self.send_data((y >> 8) as u8)?; - self.send_data(y as u8)?; - - self.send_data(((y + l - 1) >> 8) as u8)?; - self.send_data((y + l - 1) as u8)?; - - self.send_data(0x01)?; // Gates scan both inside and outside of the partial window. (default) - - if is_dtm1 { - self.send_command(Command::DATA_START_TRANSMISSION_1)? - } else { - self.send_command(Command::DATA_START_TRANSMISSION_2)? - } + self.send_command(Command::POWER_OFF)?; + self.wait_until_idle(); + self.send_command(Command::DEEP_SLEEP)?; + self.send_data(0xA5)?; - self.send_multiple_data(buffer)?; + Ok(()) + } - self.send_command(Command::PARTIAL_OUT) + fn reset(&mut self) { + self.interface.reset() } - + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } - // void DisplayFrame(const unsigned char* frame_buffer); - /// Display the frame data from SRAM - /// Uses the SLOW!! full update/refresh - /// Default color: 0xff - /// - pub fn display_and_transfer_frame(&mut self, buffer: &[u8], color: Option) -> Result<(), E>{ - let color = color.unwrap_or(0xff); + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E> { + let color_value = self.color.get_byte_value(); self.send_resolution()?; @@ -272,10 +226,9 @@ where //self.send_command_u8(0x97)?; //VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 self.send_data(0x97)?; - self.send_command(Command::DATA_START_TRANSMISSION_1)?; for _ in 0..(buffer.len()) { - self.send_data(color)?; + self.send_data(color_value)?; } self.delay_ms(2); @@ -284,73 +237,79 @@ where for &elem in buffer.iter() { self.send_data(elem)?; } - self.delay_ms(2); - - self.set_lut()?; - - self.send_command(Command::DISPLAY_REFRESH)?; - //TODO: adapt time, is this long delay really needed? - self.delay_ms(10); - self.wait_until_idle(); Ok(()) } - fn send_resolution(&mut self) -> Result<(), E> { - let w = self.get_width(); - let h = self.get_height(); + fn update_partial_frame( + &mut self, + buffer: &[u8], + x: u16, + y: u16, + width: u16, + height: u16, + ) -> Result<(), E> { + if buffer.len() as u16 != width / 8 * height { + //TODO: panic!! or sth like that + //return Err("Wrong buffersize"); + } - self.send_command(Command::RESOLUTION_SETTING)?; - self.send_data((w >> 8) as u8)?; - self.send_data(w as u8)?; - self.send_data((h >> 8) as u8)?; - self.send_data(h as u8) - } + self.send_command(Command::PARTIAL_IN)?; + self.send_command(Command::PARTIAL_WINDOW)?; + self.send_data((x >> 8) as u8)?; + let tmp = x & 0xf8; + self.send_data(tmp as u8)?; // x should be the multiple of 8, the last 3 bit will always be ignored + let tmp = tmp + width - 1; + self.send_data((tmp >> 8) as u8)?; + self.send_data((tmp | 0x07) as u8)?; - /// Displays the frame data from SRAM - pub fn display_frame(&mut self) -> Result<(), E> { - self.set_lut()?; - self.send_command(Command::DISPLAY_REFRESH)?; + self.send_data((y >> 8) as u8)?; + self.send_data(y as u8)?; - self.delay_ms(100); - self.wait_until_idle(); - Ok(()) + self.send_data(((y + height - 1) >> 8) as u8)?; + self.send_data((y + height - 1) as u8)?; + + self.send_data(0x01)?; // Gates scan both inside and outside of the partial window. (default) + + //TODO: handle dtm somehow + let is_dtm1 = false; + if is_dtm1 { + self.send_command(Command::DATA_START_TRANSMISSION_1)? + } else { + self.send_command(Command::DATA_START_TRANSMISSION_2)? + } + + self.send_multiple_data(buffer)?; + + self.send_command(Command::PARTIAL_OUT) } - /// Same as display_frame(), but with nearly no delay - /// and uses the fast/partial refresh LUT - /// needs more testing!!! - /// maybe delay can be fully removed as wait_until_idle should do - /// the necessary stuff - /// TODO: check delay!!! - /// Displays the frame data from SRAM - pub fn display_frame_quick(&mut self) -> Result<(), E> { - self.set_lut_quick()?; + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.update_frame(buffer)?; + self.display_frame() + } + + + fn display_frame(&mut self) -> Result<(), E> { self.send_command(Command::DISPLAY_REFRESH)?; - self.delay_ms(1); self.wait_until_idle(); Ok(()) } - - /// Clears the frame from the buffer - /// - /// Set a reset_color if you want a different from the default 0xff - /// - /// TODO: should that option be removed? E.g. the struct contains an additional default background value - /// which is settable? - pub fn clear_frame(&mut self, reset_color: Option) -> Result<(), E> { - let reset_color: Color = reset_color.unwrap_or(Color::White); + // TODO: add this abstraction function + // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; + fn clear_frame(&mut self) -> Result<(), E> { self.send_resolution()?; let size = self.width / 8 * self.height; + let color_value = self.color.get_byte_value(); self.send_command(Command::DATA_START_TRANSMISSION_1)?; self.delay_ms(2); for _ in 0..size { - self.send_data(reset_color.get_byte_value())?; + self.send_data(color_value)?; } self.delay_ms(2); @@ -358,203 +317,101 @@ where self.send_command(Command::DATA_START_TRANSMISSION_2)?; self.delay_ms(2); for _ in 0..size { - self.send_data(reset_color.get_byte_value())?; + self.send_data(color_value)?; } Ok(()) } - /// Let the device enter deep-sleep mode to save power. - /// - /// The deep sleep mode returns to standby with a hardware reset. - /// But you can also use [EPD4in2::reset()](EPD4in2::reset()) to awaken. - /// But as you need to power it up once more anyway you can also just directly use [EPD4in2::init()](EPD4in2::init()) for resetting - /// and initialising which already contains the reset - pub fn sleep(&mut self) -> Result<(), E> { - self.send_command(Command::VCOM_AND_DATA_INTERVAL_SETTING)?; - self.send_data(0x17)?; //border floating - self.send_command(Command::VCM_DC_SETTING)?; // VCOM to 0V - self.send_command(Command::PANEL_SETTING)?; - self.delay_ms(100); - - self.send_command(Command::POWER_SETTING)?; //VG&VS to 0V fast - for _ in 0..4 { - self.send_data(0x00)?; - } - self.delay_ms(100); - - self.send_command(Command::POWER_OFF)?; - self.wait_until_idle(); - self.send_command(Command::DEEP_SLEEP)?; - self.send_data(0xA5)?; - - Ok(()) + /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) + fn set_background_color(&mut self, color: Color) { + self.color = color; } +} - /// Resets the device. - /// - /// Often used to awake the module from deep sleep. See [EPD4in2::sleep()](EPD4in2::sleep()) - /// - /// TODO: Takes at least 400ms of delay alone, can it be shortened? - pub fn reset(&mut self) { - self.rst.set_low(); +impl EPD4in2 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + D: DelayUs + DelayMs, +{ + fn send_command(&mut self, command: Command) -> Result<(), E> { + self.interface.send_command(command) + } - //TODO: why 200ms? (besides being in the waveshare code) - self.delay_ms(200); + fn send_data(&mut self, val: u8) -> Result<(), E> { + self.interface.send_data(val) + } - self.rst.set_high(); + fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> { + self.interface.send_multiple_data(data) + } - //TODO: same as 3 lines above - self.delay_ms(200); + fn wait_until_idle(&mut self) { + self.interface.wait_until_idle(true) } + fn send_resolution(&mut self) -> Result<(), E> { + let w = self.get_width(); + let h = self.get_height(); + self.send_command(Command::RESOLUTION_SETTING)?; + self.send_data((w >> 8) as u8)?; + self.send_data(w as u8)?; + self.send_data((h >> 8) as u8)?; + self.send_data(h as u8) + } /// Fill the look-up table for the EPD - //TODO: make public? + //TODO: make public? fn set_lut(&mut self) -> Result<(), E> { - self.set_lut_helper( - &LUT_VCOM0, - &LUT_WW, - &LUT_BW, - &LUT_WB, - &LUT_BB) + self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) } /// Fill the look-up table for a quick display (partial refresh) - /// - /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) - /// //TODO: make public? + /// + /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) + /// //TODO: make public? fn set_lut_quick(&mut self) -> Result<(), E> { self.set_lut_helper( &LUT_VCOM0_QUICK, &LUT_WW_QUICK, &LUT_BW_QUICK, &LUT_WB_QUICK, - &LUT_BB_QUICK) + &LUT_BB_QUICK, + ) } - fn set_lut_helper(&mut self, - lut_vcom: &[u8], - lut_ww: &[u8], - lut_bw: &[u8], - lut_wb: &[u8], - lut_bb: &[u8]) -> Result<(), E> - { - //vcom + fn set_lut_helper( + &mut self, + lut_vcom: &[u8], + lut_ww: &[u8], + lut_bw: &[u8], + lut_wb: &[u8], + lut_bb: &[u8], + ) -> Result<(), E> { + // LUT VCOM self.send_command(Command::LUT_FOR_VCOM)?; self.send_multiple_data(lut_vcom)?; - //ww -- + // LUT WHITE to WHITE self.send_command(Command::LUT_WHITE_TO_WHITE)?; self.send_multiple_data(lut_ww)?; - //bw r + // LUT BLACK to WHITE self.send_command(Command::LUT_BLACK_TO_WHITE)?; self.send_multiple_data(lut_bw)?; - //wb w + // LUT WHITE to BLACK self.send_command(Command::LUT_WHITE_TO_BLACK)?; self.send_multiple_data(lut_wb)?; - //bb b + // LUT BLACK to BLACK self.send_command(Command::LUT_BLACK_TO_BLACK)?; self.send_multiple_data(lut_bb)?; Ok(()) } - - /// Basic function for sending [Commands](Command). - /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_data()](EPD4in2::send_data()) - /// Should rarely be needed! - /// //TODO: make public? - fn send_command(&mut self, command: Command) -> Result<(), E> { - // low for commands - self.dc.set_low(); - - // Transfer the command over spi - self.with_cs(|epd| { - epd.spi.write(&[command.addr()]) - }) - } - - /// Basic function for sending a single u8 of data over spi - /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_command()](EPD4in2::send_command()) - /// - /// Should rarely be needed! - /// //TODO: make public? - fn send_data(&mut self, val: u8) -> Result<(), E> { - // high for data - self.dc.set_high(); - - // Transfer data (u8) over spi - self.with_cs(|epd| { - epd.spi.write(&[val]) - }) - } - - /// Basic function for sending an array of u8-values of data over spi - /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_command()](EPD4in2::send_command()) - /// - /// Should rarely be needed! - /// //TODO: make public? - fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> { - // high for data - self.dc.set_high(); - - // Transfer data (u8-array) over spi - self.with_cs(|epd| { - epd.spi.write(data) - }) - } - - // spi write helper/abstraction function - fn with_cs(&mut self, f: F) -> Result<(), E> - where - F: FnOnce(&mut Self) -> Result<(), E>, - { - // activate spi with cs low - self.cs.set_low(); - // transfer spi data - let result = f(self); - // deativate spi with cs high - self.cs.set_high(); - // return result - result - } - - - /// 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 - pub fn wait_until_idle(&mut self) { - //low: busy, high: idle - while self.busy.is_low() { - //TODO: shorten the time? it was 100 in the beginning - self.delay_ms(10); - } - } - - - /// Abstraction of setting the delay for simpler calls - pub fn delay_ms(&mut self, delay: u16) { - self.delay.delay_ms(delay); - } } - - - - - - - - - - - - - diff --git a/src/interface/data_interface.rs b/src/interface/connection_interface.rs similarity index 81% rename from src/interface/data_interface.rs rename to src/interface/connection_interface.rs index 2c50617..bbd7b4c 100644 --- a/src/interface/data_interface.rs +++ b/src/interface/connection_interface.rs @@ -10,7 +10,7 @@ use interface::Command; /// EPD4in2 driver /// -pub(crate) struct DataInterface { +pub(crate) struct ConnectionInterface { /// SPI spi: SPI, /// CS for SPI @@ -26,7 +26,7 @@ pub(crate) struct DataInterface { } -impl DataInterface +impl ConnectionInterface where SPI: Write, CS: OutputPin, @@ -36,12 +36,12 @@ where D: DelayUs + DelayMs, { pub(crate) fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Self { - DataInterface {spi, cs, busy, dc, rst, delay } + ConnectionInterface {spi, cs, busy, dc, rst, delay } } /// Basic function for sending [Commands](Command). /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_data()](EPD4in2::send_data()) + /// Enables direct interaction with the device with the help of [send_data()](ConnectionInterface::send_data()) /// Should rarely be needed! /// //TODO: make public? pub(crate) fn send_command(&mut self, command: T) -> Result<(), E> { @@ -56,7 +56,7 @@ where /// Basic function for sending a single u8 of data over spi /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_command()](EPD4in2::send_command()) + /// Enables direct interaction with the device with the help of [Esend_command()](ConnectionInterface::send_command()) /// /// Should rarely be needed! /// //TODO: make public? @@ -72,7 +72,7 @@ where /// Basic function for sending an array of u8-values of data over spi /// - /// Enables direct interaction with the device with the help of [EPD4in2::send_command()](EPD4in2::send_command()) + /// Enables direct interaction with the device with the help of [send_command()](EPD4in2::send_command()) /// /// Should rarely be needed! /// //TODO: make public? @@ -107,11 +107,18 @@ where /// 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 - pub(crate) fn wait_until_idle(&mut self) { + /// + /// is_busy_low + /// - TRUE for epd4in2, epd1in54, epd2in13, epd2in7, epd5in83, epd7in5 + /// + /// - FALSE for epd2in9 + /// Most likely there was a mistake with the 2in9 busy connection + pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) { + self.delay_ms(1); //low: busy, high: idle - while self.busy.is_low() { + while (is_busy_low && self.busy.is_low()) || (!is_busy_low && self.busy.is_high()) { //TODO: shorten the time? it was 100 in the beginning - self.delay_ms(10); + self.delay_ms(5); } } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index e190cf9..aab00e7 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -9,7 +9,7 @@ use hal::{ use drawing::color::Color; -pub mod data_interface; +pub mod connection_interface; //TODO: test spi mode /// SPI mode - @@ -25,6 +25,18 @@ pub(crate) trait Command { fn address(self) -> u8; } + +//TODO add LUT trait with set_fast_lut +// and set_manual_lut and set_normal_lut or sth like that +// for partial updates + +pub trait LUTSupport { + fn set_lut(&mut self) -> Result<(), Error>; + fn set_lut_quick(&mut self) -> Result<(), Error>; + fn set_lut_manual(&mut self, data: &[u8]) -> Result<(), Error>; +} + + pub trait WaveshareInterface where SPI: Write, @@ -63,16 +75,26 @@ pub trait WaveshareInterface fn init(&mut self) -> Result<(), E>; - + // void DisplayFrame(const unsigned char* frame_buffer); + /// Transmit a full frame to the SRAM of the DPD + /// fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>; + //TODO: is dtm always used? + /// Transmit partial data to the SRAM of the EPD, + /// the final parameter dtm chooses between the 2 + /// internal buffers + /// + /// Normally it should be dtm2, so use false + /// + /// BUFFER needs to be of size: w / 8 * l ! fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>; /// Displays the frame data from SRAM fn display_frame(&mut self) -> Result<(), E>; // TODO: add this abstraction function - // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; /// Clears the frame from the buffer /// @@ -102,10 +124,7 @@ pub trait WaveshareInterface fn delay_ms(&mut self, delay: u16); /* - -display_frame - -clear_frame - -set_full_frame - -set_partial_frame + // -set_quick_lut? diff --git a/src/lib.rs b/src/lib.rs index 4f8851f..5b3e1e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,8 @@ use hal::{ pub mod drawing; pub mod epd4in2; -use epd4in2::*; + + pub mod epd2in9;