From e417e03ca82ef0d5b9f6d2d0d0f34708bba0f29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Fri, 3 Aug 2018 14:17:37 +0200 Subject: [PATCH 01/22] Added a note with the two possible display "configs" to the readme --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 340e844..79eb22d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,28 @@ It's possible with this driver but might lead to ghosting / burn-in effects ther | RST | External reset pin (Low for reset) | | BUSY | Busy state output pin (Low for busy) | +### Display Configs + +There are two types of Display Configurations used in Wavedshare EPDs, which also needs to be set on the "new" E-Paper Driver HAT. +They are also called A and B, but you shouldn't get confused and mix it with the Type A,B,C and D of the various Displays, which just describe different types (colored variants) or new versions. In the Display Config the seperation is most likely due to included fast partial refresh of the displays. In a Tabular form: + +| Type A | Tybe B | +| :---: | :---: | +| 1.54in (A) | 1.54in (B) | +| 2.13in (A) | 1.54in (C) | +| 2.13in (D) | 2.13in (B) | +| 2.9in (A) | 2.13in (C) | +| | 2.7in (A) | +| | 2.7in (B) | +| | 2.9in (B) | +| | 2.9in (C) | +| | 4.2in (A) | +| | 4.2in (B) | +| | 4.2in (C) | +| | 7.5in (A) | +| | 7.5in (B) | +| | 7.5in (C) | + ## TODO's - [ ] add more examples (e.g. for f3) From 9791e1b7275b9101410ca391316e7f540faa9416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Fri, 3 Aug 2018 14:19:08 +0200 Subject: [PATCH 02/22] Renaming of a few Traits and changing the ConnectionInterface::new() variables --- src/interface/connection_interface.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/interface/connection_interface.rs b/src/interface/connection_interface.rs index 7ae2cc5..4a4cad3 100644 --- a/src/interface/connection_interface.rs +++ b/src/interface/connection_interface.rs @@ -26,16 +26,16 @@ pub(crate) struct ConnectionInterface { } -impl ConnectionInterface +impl ConnectionInterface where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, - DC: OutputPin, + DataCommand: OutputPin, RST: OutputPin, - D: DelayUs + DelayMs, + Delay: DelayUs + DelayMs, { - pub(crate) fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Self { + pub(crate) fn new(spi: SPI, cs: CS, busy: BUSY, dc: DataCommand, rst: RST, delay: Delay) -> Self { ConnectionInterface {spi, cs, busy, dc, rst, delay } } @@ -44,7 +44,7 @@ where /// 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> { + pub(crate) fn send_command(&mut self, command: T) -> Result<(), ErrorSpeziale> { // low for commands self.dc.set_low(); @@ -60,7 +60,7 @@ where /// /// Should rarely be needed! /// //TODO: make public? - pub(crate) fn send_data(&mut self, val: u8) -> Result<(), E> { + pub(crate) fn send_data(&mut self, val: u8) -> Result<(), ErrorSpeziale> { // high for data self.dc.set_high(); @@ -76,7 +76,7 @@ where /// /// Should rarely be needed! /// //TODO: make public? - pub(crate) fn send_data_x_times(&mut self, val: u8, repetitions: u16) -> Result<(), E> { + pub(crate) fn send_data_x_times(&mut self, val: u8, repetitions: u16) -> Result<(), ErrorSpeziale> { // high for data self.dc.set_high(); @@ -95,7 +95,7 @@ where /// /// Should rarely be needed! /// //TODO: make public? - pub(crate) fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> { + pub(crate) fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), ErrorSpeziale> { // high for data self.dc.set_high(); @@ -106,9 +106,9 @@ where } // spi write helper/abstraction function - pub(crate) fn with_cs(&mut self, f: F) -> Result<(), E> + pub(crate) fn with_cs(&mut self, f: F) -> Result<(), ErrorSpeziale> where - F: FnOnce(&mut Self) -> Result<(), E>, + F: FnOnce(&mut Self) -> Result<(), ErrorSpeziale>, { // activate spi with cs low self.cs.set_low(); @@ -129,8 +129,8 @@ where /// /// is_busy_low /// - /// - TRUE for epd4in2, epd1in54, epd2in13, epd2in7, epd5in83, epd7in5 - /// - FALSE for epd2in9 + /// - 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 pub(crate) fn wait_until_idle(&mut self, is_busy_low: bool) { From bbb79560f6efa5e70b95f026dcd3dc1af0b5631e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Fri, 3 Aug 2018 14:21:32 +0200 Subject: [PATCH 03/22] moved duplicate type a constants, commands and configs to a new, more generalised type_a module, not completly finished yet --- src/epd1in54/mod.rs | 3 + src/epd2in9/mod.rs | 313 +-------------------------- src/lib.rs | 2 + src/{epd2in9 => type_a}/command.rs | 11 +- src/{epd2in9 => type_a}/constants.rs | 3 - src/type_a/mod.rs | 306 ++++++++++++++++++++++++++ 6 files changed, 318 insertions(+), 320 deletions(-) create mode 100644 src/epd1in54/mod.rs rename src/{epd2in9 => type_a}/command.rs (86%) rename src/{epd2in9 => type_a}/constants.rs (88%) create mode 100644 src/type_a/mod.rs diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs new file mode 100644 index 0000000..fdc94fd --- /dev/null +++ b/src/epd1in54/mod.rs @@ -0,0 +1,3 @@ +pub(crate) const WIDTH: u16 = 128; +pub(crate) const HEIGHT: u16 = 296; +pub(crate) const DPI: u16 = 184; \ No newline at end of file diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 6b0a7cd..e9d1f00 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -1,311 +1,2 @@ -//! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI -//! -//! -//! # Examples from the 4.2" Display. It should work the same for the 2.9" one. -//! -//! ```ignore -//! 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(); -//! ``` - - -use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* -}; - -mod constants; -use self::constants::*; - -use drawing::color::Color; - -pub mod command; -pub use self::command::Command; - -use interface::*; - -use interface::connection_interface::ConnectionInterface; - - - -/// EPD2in9 driver -/// -pub struct EPD2in9 { - /// SPI - interface: ConnectionInterface, - /// Width - width: u16, - /// Height - height: u16, - /// Color - background_color: Color, -} - -impl EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, - D: DelayUs + DelayMs -{ - -} - - -impl WaveshareInterface for EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, - D: DelayUs + DelayMs, -{ - - fn get_width(&self) -> u16 { - self.width - } - - fn get_height(&self) -> u16 { - self.height - } - - - fn new( - spi: SPI, - cs: CS, - busy: BUSY, - dc: DC, - rst: RST, - delay: D - ) -> Result { - let width = WIDTH as u16; - let height = HEIGHT as u16; - - let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); - - let background_color = Color::White; - - let mut epd = EPD2in9 {interface, width, height, background_color}; - - - epd.init()?; - - Ok(epd) - } - - - - fn init(&mut self) -> Result<(), E> { - - - self.reset(); - - // 3 Databytes: - // A[7:0] - // 0.. A[8] - // 0.. B[2:0] - // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) - self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; - self.interface.send_data(HEIGHT as u8)?; - self.interface.send_data((HEIGHT >> 8) as u8)?; - self.interface.send_data(0x00)?; - - // 3 Databytes: (and default values from datasheet and arduino) - // 1 .. A[6:0] = 0xCF | 0xD7 - // 1 .. B[6:0] = 0xCE | 0xD6 - // 1 .. C[6:0] = 0x8D | 0x9D - //TODO: test - self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; - self.interface.send_data(0xD7)?; - self.interface.send_data(0xD6)?; - self.interface.send_data(0x9D)?; - - // One Databyte with value 0xA8 for 7V VCOM - self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; - self.interface.send_data(0xA8)?; - - // One Databyte with default value 0x1A for 4 dummy lines per gate - self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; - self.interface.send_data(0x1A)?; - - // One Databyte with default value 0x08 for 2us per line - self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; - self.interface.send_data(0x08)?; - - // One Databyte with default value 0x03 - // -> address: x increment, y increment, address counter is updated in x direction - self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; - self.interface.send_data(0x03)?; - - self.set_lut() - } - - fn sleep(&mut self) -> Result<(), E> { - - 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.wait_until_idle(); - Ok(()) - } - - - fn reset(&mut self) { - self.interface.reset() - } - - fn delay_ms(&mut self, delay: u16) { - self.interface.delay_ms(delay) - } - - - - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.use_full_frame()?; - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) - } - - //TODO: update description: last 3 bits will be ignored for width and x_pos - fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ - self.set_ram_area(x, y, x + width, y + height)?; - self.set_ram_counter(x, y)?; - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) - } - - - fn display_frame(&mut self) -> Result<(), E>{ - // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) - //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) - self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; - self.interface.send_data(0xC4)?; - - self.interface.send_command(Command::MASTER_ACTIVATION)?; - // MASTER Activation should not be interupted to avoid currption of panel images - // therefore a terminate command is send - self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) - } - - - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.update_frame(buffer)?; - self.display_frame() - } - - - fn clear_frame(&mut self) -> Result<(), E>{ - self.use_full_frame()?; - - // clear the ram with the background color - let color = self.background_color.get_byte_value(); - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) - } - - /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) - fn set_background_color(&mut self, background_color: Color){ - self.background_color = background_color; - } - -} - -impl EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, - D: DelayUs + DelayMs, -{ - fn wait_until_idle(&mut self) { - self.interface.wait_until_idle(false); - } - - pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { - // choose full frame/ram - self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; - - // start from the beginning - self.set_ram_counter(0,0) - } - - pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { - assert!(start_x < end_x); - assert!(start_y < end_y); - - // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; - self.interface.send_data((start_x >> 3) as u8)?; - self.interface.send_data((end_x >> 3) as u8)?; - - // 2 Databytes: A[7:0] & 0..A[8] for each - start and end - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; - self.interface.send_data(start_y as u8)?; - self.interface.send_data((start_y >> 8) as u8)?; - self.interface.send_data(end_y as u8)?; - self.interface.send_data((end_y >> 8) as u8) - } - - pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { - // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; - self.interface.send_data((x >> 3) as u8)?; - - // 2 Databytes: A[7:0] & 0..A[8] - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; - self.interface.send_data(y as u8)?; - self.interface.send_data((y >> 8) as u8)?; - - self.wait_until_idle(); - Ok(()) - } - - /// Uses the slower full update - pub fn set_lut(&mut self) -> Result<(), E> { - self.set_lut_helper(&LUT_FULL_UPDATE) - } - - /// Uses the quick partial refresh - pub fn set_lut_quick(&mut self) -> Result<(), E> { - self.set_lut_helper(&LUT_PARTIAL_UPDATE) - } - - //TODO: assert length for LUT is exactly 30 - fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { - self.set_lut_helper(buffer) - } - - - fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { - assert!(buffer.len() == 30); - self.interface.send_command(Command::WRITE_LUT_REGISTER)?; - self.interface.send_multiple_data(buffer) - } - -} \ No newline at end of file +pub(crate) const WIDTH: u16 = 128; +pub(crate) const HEIGHT: u16 = 296; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a6558f8..28493fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,8 @@ pub mod epd2in9; pub mod interface; +pub mod type_a; + //TODO: test spi mode diff --git a/src/epd2in9/command.rs b/src/type_a/command.rs similarity index 86% rename from src/epd2in9/command.rs rename to src/type_a/command.rs index 016257b..884adc8 100644 --- a/src/epd2in9/command.rs +++ b/src/type_a/command.rs @@ -1,15 +1,13 @@ -//! SPI Commands for the Waveshare 2.9" E-Ink Display +//! SPI Commands for the Waveshare 2.9" and 1.54" E-Ink Display use interface; -/// EPD2IN9 commands +/// EPD1in54 and EPD2IN9 commands /// /// Should rarely (never?) be needed directly. /// /// 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(non_camel_case_types)] #[derive(Copy, Clone)] @@ -28,6 +26,7 @@ pub enum Command { /// 1.. C[6:0] /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D BOOSTER_SOFT_START_CONTROL = 0x0C, + GATE_SCAN_START_POSITION = 0x0F, //TODO: useful? // GATE_SCAN_START_POSITION = 0x0F, /// Deep Sleep Mode Control @@ -92,8 +91,8 @@ mod tests { fn command_addr() { assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01); - //assert_eq!(Command::PANEL_SETTING.addr(), 0x00); + assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.addr(), 0x4E); - //assert_eq!(Command::DISPLAY_REFRESH.addr(), 0x12); + assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.addr(), 0xFF); } } \ No newline at end of file diff --git a/src/epd2in9/constants.rs b/src/type_a/constants.rs similarity index 88% rename from src/epd2in9/constants.rs rename to src/type_a/constants.rs index 98e7b05..63de296 100644 --- a/src/epd2in9/constants.rs +++ b/src/type_a/constants.rs @@ -1,6 +1,3 @@ -pub(crate) const WIDTH: u16 = 128; -pub(crate) const HEIGHT: u16 = 296; - // Original Waveforms from Waveshare pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[ 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, diff --git a/src/type_a/mod.rs b/src/type_a/mod.rs new file mode 100644 index 0000000..5f66f99 --- /dev/null +++ b/src/type_a/mod.rs @@ -0,0 +1,306 @@ +//! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI +//! +//! +//! # Examples from the 4.2" Display. It should work the same for the 2.9" one. +//! +//! ```ignore +//! 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(); +//! ``` + + +use hal::{ + blocking::{ + spi::Write, + delay::* + }, + digital::* +}; + +mod constants; +use self::constants::*; + +use drawing::color::Color; + +pub mod command; +pub use self::command::Command; + +use interface::*; + +use interface::connection_interface::ConnectionInterface; + + + +/// EPD2in9 driver +/// +pub struct EPD2in9 { + /// SPI + interface: ConnectionInterface, + /// Width + width: u16, + /// Height + height: u16, + /// Color + background_color: Color, +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs +{ + +} + + +impl WaveshareInterface + for EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + + fn get_width(&self) -> u16 { + self.width + } + + fn get_height(&self) -> u16 { + self.height + } + + + fn new( + interface: ConnectionInterface, + display: Displays + ) -> Result { + let width = WIDTH as u16; + let height = HEIGHT as u16; + + let background_color = Color::White; + + let mut epd = EPD2in9 {interface, width, height, background_color}; + + + epd.init()?; + + Ok(epd) + } + + + + fn init(&mut self) -> Result<(), E> { + + + self.reset(); + + // 3 Databytes: + // A[7:0] + // 0.. A[8] + // 0.. B[2:0] + // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) + self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; + self.interface.send_data(HEIGHT as u8)?; + self.interface.send_data((HEIGHT >> 8) as u8)?; + self.interface.send_data(0x00)?; + + // 3 Databytes: (and default values from datasheet and arduino) + // 1 .. A[6:0] = 0xCF | 0xD7 + // 1 .. B[6:0] = 0xCE | 0xD6 + // 1 .. C[6:0] = 0x8D | 0x9D + //TODO: test + self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; + self.interface.send_data(0xD7)?; + self.interface.send_data(0xD6)?; + self.interface.send_data(0x9D)?; + + // One Databyte with value 0xA8 for 7V VCOM + self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; + self.interface.send_data(0xA8)?; + + // One Databyte with default value 0x1A for 4 dummy lines per gate + self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; + self.interface.send_data(0x1A)?; + + // One Databyte with default value 0x08 for 2us per line + self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; + self.interface.send_data(0x08)?; + + // One Databyte with default value 0x03 + // -> address: x increment, y increment, address counter is updated in x direction + self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; + self.interface.send_data(0x03)?; + + self.set_lut() + } + + fn sleep(&mut self) -> Result<(), E> { + + 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.wait_until_idle(); + Ok(()) + } + + + fn reset(&mut self) { + self.interface.reset() + } + + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } + + + + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.use_full_frame()?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + //TODO: update description: last 3 bits will be ignored for width and x_pos + fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ + self.set_ram_area(x, y, x + width, y + height)?; + self.set_ram_counter(x, y)?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + + fn display_frame(&mut self) -> Result<(), E>{ + // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) + //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) + self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; + self.interface.send_data(0xC4)?; + + self.interface.send_command(Command::MASTER_ACTIVATION)?; + // MASTER Activation should not be interupted to avoid currption of panel images + // therefore a terminate command is send + self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) + } + + + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.update_frame(buffer)?; + self.display_frame() + } + + + fn clear_frame(&mut self) -> Result<(), E>{ + self.use_full_frame()?; + + // clear the ram with the background color + let color = self.background_color.get_byte_value(); + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + } + + /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) + fn set_background_color(&mut self, background_color: Color){ + self.background_color = background_color; + } + +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + D: DelayUs + DelayMs, +{ + fn wait_until_idle(&mut self) { + self.interface.wait_until_idle(false); + } + + pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { + // choose full frame/ram + self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; + + // start from the beginning + self.set_ram_counter(0,0) + } + + pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { + assert!(start_x < end_x); + assert!(start_y < end_y); + + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; + self.interface.send_data((start_x >> 3) as u8)?; + self.interface.send_data((end_x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] for each - start and end + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; + self.interface.send_data(start_y as u8)?; + self.interface.send_data((start_y >> 8) as u8)?; + self.interface.send_data(end_y as u8)?; + self.interface.send_data((end_y >> 8) as u8) + } + + pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; + self.interface.send_data((x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; + self.interface.send_data(y as u8)?; + self.interface.send_data((y >> 8) as u8)?; + + self.wait_until_idle(); + Ok(()) + } + + /// Uses the slower full update + pub fn set_lut(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_FULL_UPDATE) + } + + /// Uses the quick partial refresh + pub fn set_lut_quick(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_PARTIAL_UPDATE) + } + + //TODO: assert length for LUT is exactly 30 + fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { + self.set_lut_helper(buffer) + } + + + fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { + assert!(buffer.len() == 30); + self.interface.send_command(Command::WRITE_LUT_REGISTER)?; + self.interface.send_multiple_data(buffer) + } + +} \ No newline at end of file From 36aa5118fc4f3b5ee560fe9107550dc780fdb43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Fri, 3 Aug 2018 14:22:18 +0200 Subject: [PATCH 04/22] added a new file to contain the information about the various displays --- src/epds.rs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/epds.rs diff --git a/src/epds.rs b/src/epds.rs new file mode 100644 index 0000000..3fbe435 --- /dev/null +++ b/src/epds.rs @@ -0,0 +1,2 @@ + +//TODO: fill From c4ba7ffb21d8fb276342454c1ae9eb36ea434b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Fri, 3 Aug 2018 14:23:09 +0200 Subject: [PATCH 05/22] Added a new Display Trait, but that is still in work and renamed a few other traitnames to make them more clear --- src/epd4in2/mod.rs | 23 +++++++++++++---------- src/interface/mod.rs | 38 ++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 0f5ee76..a24ff31 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -73,7 +73,8 @@ pub const SPI_MODE: Mode = Mode { /// EPD4in2 driver /// -pub struct EPD4in2 { +pub struct EPD4in2 + { /// Connection Interface interface: ConnectionInterface, /// Width @@ -84,15 +85,17 @@ pub struct EPD4in2 { color: Color, } -impl WaveshareInterface - for EPD4in2 -where - SPI: Write, + + +impl WaveshareInterface + for EPD4in2 +where + SPI: Write, CS: OutputPin, BUSY: InputPin, - DC: OutputPin, + DataCommand: OutputPin, RST: OutputPin, - D: DelayUs + DelayMs, + Delay: DelayUs + DelayMs, { fn get_width(&self) -> u16 { self.width @@ -117,11 +120,11 @@ where /// /// epd4in2.sleep(); /// ``` - fn new(spi: SPI, cs: CS, busy: BUSY, dc: DC, rst: RST, delay: D) -> Result { + fn new(interface: ConnectionInterface) -> Result { let width = WIDTH as u16; let height = HEIGHT as u16; - let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); + let color = Color::White; let mut epd = EPD4in2 { interface, @@ -135,7 +138,7 @@ where Ok(epd) } - fn init(&mut self) -> Result<(), E> { + fn init(&mut self) -> Result<(), Error> { // reset the device self.reset(); diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 0bf783a..308ced9 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -12,6 +12,7 @@ use drawing::color::Color; /// Interface for the physical connection between display and the controlling device pub mod connection_interface; +use self::connection_interface::ConnectionInterface; /// All commands need to have this trait which gives the address of the command @@ -21,6 +22,12 @@ pub(crate) trait Command { } +pub trait Displays { + fn width(self) -> u8; + fn height(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 trait LUTSupport { @@ -30,14 +37,14 @@ trait LUTSupport { } -pub trait WaveshareInterface +pub trait WaveshareInterface where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, - DC: OutputPin, + DataCommand: OutputPin, RST: OutputPin, - D: DelayUs + DelayMs, + Delay: DelayUs + DelayMs, { /// Get the width of the display fn get_width(&self) -> u16; @@ -49,13 +56,8 @@ pub trait WaveshareInterface /// /// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards fn new( - spi: SPI, - cs: CS, - busy: BUSY, - dc: DC, - rst: RST, - delay: D - ) -> Result + interface: ConnectionInterface, + ) -> Result where Self: Sized; /// This initialises the EPD and powers it up @@ -65,13 +67,13 @@ pub trait WaveshareInterface /// This function calls [reset()](WaveshareInterface::reset()), /// so you don't need to call reset your self when trying to wake your device up /// after setting it to sleep. - fn init(&mut self) -> Result<(), E>; + fn init(&mut self) -> Result<(), Error>; // 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>; + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), Error>; //TODO: is dtm always used? /// Transmit partial data to the SRAM of the EPD, @@ -81,18 +83,18 @@ pub trait WaveshareInterface /// 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>; + fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), Error>; /// Displays the frame data from SRAM - fn display_frame(&mut self) -> Result<(), E>; + fn display_frame(&mut self) -> Result<(), Error>; // 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<(), Error>; /// Clears the frame from the buffer /// /// Uses the chosen background color - fn clear_frame(&mut self) -> Result<(), E>; + fn clear_frame(&mut self) -> Result<(), Error>; /// Sets the backgroundcolor for various commands like [clear_frame()](WaveshareInterface::clear_frame()) fn set_background_color(&mut self, color: Color); @@ -104,7 +106,7 @@ pub trait WaveshareInterface /// But you can also use [reset()](WaveshareInterface::reset()) to awaken. /// But as you need to power it up once more anyway you can also just directly use [init()](WaveshareInterface::init()) for resetting /// and initialising which already contains the reset - fn sleep(&mut self) -> Result<(), E>; + fn sleep(&mut self) -> Result<(), Error>; /// Resets the device. /// From 38f446304b4e52da8fb305ce16fb5d51794a5068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 09:45:43 +0200 Subject: [PATCH 06/22] removed duplicate spi_mode and improved naming of spi_error type --- src/epd4in2/mod.rs | 47 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index a24ff31..7b2d49c 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -63,13 +63,7 @@ use drawing::color::Color; pub mod command; pub use self::command::Command; -//TODO: test 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, -}; +use epds::EPD; /// EPD4in2 driver /// @@ -87,10 +81,10 @@ pub struct EPD4in2 -impl WaveshareInterface +impl WaveshareInterface for EPD4in2 where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, DataCommand: OutputPin, @@ -120,7 +114,7 @@ where /// /// epd4in2.sleep(); /// ``` - fn new(interface: ConnectionInterface) -> Result { + fn new(interface: ConnectionInterface, epd: EPD) -> Result { let width = WIDTH as u16; let height = HEIGHT as u16; @@ -138,7 +132,7 @@ where Ok(epd) } - fn init(&mut self) -> Result<(), Error> { + fn init(&mut self) -> Result<(), SPI_Error> { // reset the device self.reset(); @@ -185,7 +179,7 @@ where Ok(()) } - fn sleep(&mut self) -> Result<(), E> { + fn sleep(&mut self) -> Result<(), SPI_Error> { 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 @@ -214,7 +208,7 @@ where self.interface.delay_ms(delay) } - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E> { + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SPI_Error> { let color_value = self.color.get_byte_value(); self.send_resolution()?; @@ -250,7 +244,8 @@ where y: u16, width: u16, height: u16, - ) -> Result<(), E> { + ) -> Result<(), SPI_Error> { + if buffer.len() as u16 != width / 8 * height { //TODO: panic!! or sth like that //return Err("Wrong buffersize"); @@ -286,13 +281,13 @@ where self.send_command(Command::PARTIAL_OUT) } - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), SPI_Error>{ self.update_frame(buffer)?; self.display_frame() } - fn display_frame(&mut self) -> Result<(), E> { + fn display_frame(&mut self) -> Result<(), SPI_Error> { self.send_command(Command::DISPLAY_REFRESH)?; self.wait_until_idle(); @@ -302,7 +297,7 @@ where // TODO: add this abstraction function // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; - fn clear_frame(&mut self) -> Result<(), E> { + fn clear_frame(&mut self) -> Result<(), SPI_Error> { self.send_resolution()?; let size = self.width / 8 * self.height; @@ -330,24 +325,24 @@ where } } -impl EPD4in2 +impl EPD4in2 where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, DC: OutputPin, RST: OutputPin, D: DelayUs + DelayMs, { - fn send_command(&mut self, command: Command) -> Result<(), E> { + fn send_command(&mut self, command: Command) -> Result<(), SPI_Error> { self.interface.send_command(command) } - fn send_data(&mut self, val: u8) -> Result<(), E> { + fn send_data(&mut self, val: u8) -> Result<(), SPI_Error> { self.interface.send_data(val) } - fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> { + fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SPI_Error> { self.interface.send_multiple_data(data) } @@ -355,7 +350,7 @@ where self.interface.wait_until_idle(true) } - fn send_resolution(&mut self) -> Result<(), E> { + fn send_resolution(&mut self) -> Result<(), SPI_Error> { let w = self.get_width(); let h = self.get_height(); @@ -368,7 +363,7 @@ where /// Fill the look-up table for the EPD //TODO: make public? - fn set_lut(&mut self) -> Result<(), E> { + fn set_lut(&mut self) -> Result<(), SPI_Error> { self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) } @@ -377,7 +372,7 @@ where /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) /// //TODO: make public? #[cfg(feature = "epd4in2_fast_update")] - fn set_lut_quick(&mut self) -> Result<(), E> { + fn set_lut_quick(&mut self) -> Result<(), SPI_Error> { self.set_lut_helper( &LUT_VCOM0_QUICK, &LUT_WW_QUICK, @@ -394,7 +389,7 @@ where lut_bw: &[u8], lut_wb: &[u8], lut_bb: &[u8], - ) -> Result<(), E> { + ) -> Result<(), SPI_Error> { // LUT VCOM self.send_command(Command::LUT_FOR_VCOM)?; self.send_multiple_data(lut_vcom)?; From 230fb947cbb62e325211c0124750aa691633be70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 10:03:35 +0200 Subject: [PATCH 07/22] Added a general display description struct (epds::epd) --- src/epds.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/epds.rs b/src/epds.rs index 3fbe435..d348ddb 100644 --- a/src/epds.rs +++ b/src/epds.rs @@ -1,2 +1,21 @@ -//TODO: fill +/// A struct containing necessary info about a epd (electronic paper display). E.g: +/// +/// - Width +/// - Height +/// ... +/// +/// This needs to be implemented by each new Display +pub struct EPD { + pub(crate) width: u16, + pub(crate) height: u16 + //displayrotation? +} + +impl EPD { + pub(crate) fn new(width: u16, height: u16) -> EPD { + EPD {width, height} + } + + +} \ No newline at end of file From 6422142133216c4f938c9e908df1067ef334281d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 13:34:33 +0200 Subject: [PATCH 08/22] Not all Type A screens are completly the same to the change to bring it all together was reverted. --- README.md | 6 +- examples/embedded_linux/src/main.rs | 11 +- src/epd1in54/mod.rs | 313 +++++++++++++++++++++++++- src/epd2in9/mod.rs | 310 ++++++++++++++++++++++++- src/epd4in2/constants.rs | 4 +- src/epd4in2/mod.rs | 47 ++-- src/epds.rs | 5 +- src/interface/connection_interface.rs | 4 +- src/interface/mod.rs | 5 +- src/lib.rs | 3 +- src/type_a/command.rs | 4 +- src/type_a/{constants.rs => luts.rs} | 0 src/type_a/mod.rs | 39 ++-- 13 files changed, 689 insertions(+), 62 deletions(-) rename src/type_a/{constants.rs => luts.rs} (100%) diff --git a/README.md b/README.md index 68eb5fc..85524e4 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,16 @@ This library contains a driver for E-Paper Modules from Waveshare. -Support for more than the 4.2" EPD (especially the smaller and faster ones) is in the work. +Support for more than the 4.2in EPD (especially the smaller and faster ones) is in the work. + +The 2.9in (A) and 1.54 (A) variant should both work but aren't tested yet. ## (Supported) Devices | Device (with Link) | Colors | Flexible Display | Partial Refresh | Supported | Tested | | :---: | --- | :---: | :---: | :---: | :---: | | [4.2 Inch B/W (A)](https://www.waveshare.com/product/4.2inch-e-paper-module.htm) | Black, White | ✕ | Not officially [[1](#42-inch-e-ink-blackwhite)] | ✔ | ✔ | -| [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | | | +| [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | ✔ | | | [2.13 Inch B/W (A)](https://www.waveshare.com/product/2.13inch-e-paper-hat.htm) | Black, White | ✕ | ✔ | | | | [2.9 Inch B/W (A)](https://www.waveshare.com/product/2.9inch-e-paper-module.htm) | Black, White | ✕ | ✔ | ✔ | | diff --git a/examples/embedded_linux/src/main.rs b/examples/embedded_linux/src/main.rs index ced4d9d..9716463 100644 --- a/examples/embedded_linux/src/main.rs +++ b/examples/embedded_linux/src/main.rs @@ -5,7 +5,13 @@ extern crate linux_embedded_hal as lin_hal; extern crate eink_waveshare_rs; -use eink_waveshare_rs::{epd4in2::EPD4in2, drawing::{Graphics, color::Color}, interface::WaveshareInterface}; +use eink_waveshare_rs::{ + epd4in2::{EPD4in2, self}, + drawing::{Graphics, color::Color}, + interface::{ + WaveshareInterface, + connection_interface::ConnectionInterface}, +}; use lin_hal::spidev::{self, SpidevOptions}; use lin_hal::{Pin, Spidev}; @@ -99,7 +105,8 @@ fn main() { //TODO: wait for Digital::InputPin //fixed currently with the HackInputPin, see further above - let mut epd4in2 = EPD4in2::new(spi, cs, busy_in, dc, rst, delay).expect("eink inialize error"); + let connection_interface = ConnectionInterface::new(spi, cs, busy_in, dc, rst, delay); + let mut epd4in2 = EPD4in2::new(connection_interface, epd4in2::new()).expect("eink inialize error"); //let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; let mut buffer = [0u8; 15000]; diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index fdc94fd..a9aab64 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -1,3 +1,310 @@ -pub(crate) const WIDTH: u16 = 128; -pub(crate) const HEIGHT: u16 = 296; -pub(crate) const DPI: u16 = 184; \ No newline at end of file +//! A simple Driver for the Waveshare 1.54" E-Ink Display via SPI +//! +//! +//! # Examples from the 4.2" Display. It should work the same for the 1.54" one. +//! +//! ```ignore +//! 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(); +//! ``` + +const WIDTH: u16 = 200; +const HEIGHT: u16 = 200; +const DPI: u16 = 184; + + +use epds::EPD; + + +use hal::{ + blocking::{ + spi::Write, + delay::* + }, + digital::* +}; + + +use type_a::luts::*; +pub use type_a::command::Command; + +use drawing::color::Color; + + + + +use interface::*; + +use interface::connection_interface::ConnectionInterface; + + + + +/// EPD2in9 driver +/// +pub struct EPD2in9 { + /// SPI + interface: ConnectionInterface, + /// EPD (width, height) + //epd: EPD, + /// Color + background_color: Color, +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs +{ + +} + + +impl WaveshareInterface + for EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + + fn get_width(&self) -> u16 { + WIDTH + } + + fn get_height(&self) -> u16 { + HEIGHT + } + + + fn new( + interface: ConnectionInterface + ) -> Result { + let epd = EPD::new(WIDTH, HEIGHT); + let background_color = Color::White; + + let mut epd = EPD2in9 {interface, /*epd,*/ background_color}; + + + epd.init()?; + + Ok(epd) + } + + + + fn init(&mut self) -> Result<(), E> { + + + self.reset(); + + // 3 Databytes: + // A[7:0] + // 0.. A[8] + // 0.. B[2:0] + // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) + self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; + self.interface.send_data(HEIGHT as u8)?; + self.interface.send_data((HEIGHT >> 8) as u8)?; + self.interface.send_data(0x00)?; + + // 3 Databytes: (and default values from datasheet and arduino) + // 1 .. A[6:0] = 0xCF | 0xD7 + // 1 .. B[6:0] = 0xCE | 0xD6 + // 1 .. C[6:0] = 0x8D | 0x9D + //TODO: test + self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; + self.interface.send_data(0xD7)?; + self.interface.send_data(0xD6)?; + self.interface.send_data(0x9D)?; + + // One Databyte with value 0xA8 for 7V VCOM + self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; + self.interface.send_data(0xA8)?; + + // One Databyte with default value 0x1A for 4 dummy lines per gate + self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; + self.interface.send_data(0x1A)?; + + // One Databyte with default value 0x08 for 2us per line + self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; + self.interface.send_data(0x08)?; + + // One Databyte with default value 0x03 + // -> address: x increment, y increment, address counter is updated in x direction + self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; + self.interface.send_data(0x03)?; + + self.set_lut() + } + + fn sleep(&mut self) -> Result<(), E> { + + 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.wait_until_idle(); + Ok(()) + } + + + fn reset(&mut self) { + self.interface.reset() + } + + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } + + + + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.use_full_frame()?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + //TODO: update description: last 3 bits will be ignored for width and x_pos + fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ + self.set_ram_area(x, y, x + width, y + height)?; + self.set_ram_counter(x, y)?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + + fn display_frame(&mut self) -> Result<(), E>{ + // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) + //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) + self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; + self.interface.send_data(0xC4)?; + + self.interface.send_command(Command::MASTER_ACTIVATION)?; + // MASTER Activation should not be interupted to avoid currption of panel images + // therefore a terminate command is send + self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) + } + + + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.update_frame(buffer)?; + self.display_frame() + } + + + fn clear_frame(&mut self) -> Result<(), E>{ + self.use_full_frame()?; + + // clear the ram with the background color + let color = self.background_color.get_byte_value(); + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + } + + /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) + fn set_background_color(&mut self, background_color: Color){ + self.background_color = background_color; + } + +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + D: DelayUs + DelayMs, +{ + fn wait_until_idle(&mut self) { + self.interface.wait_until_idle(false); + } + + pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { + // choose full frame/ram + self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; + + // start from the beginning + self.set_ram_counter(0,0) + } + + pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { + assert!(start_x < end_x); + assert!(start_y < end_y); + + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; + self.interface.send_data((start_x >> 3) as u8)?; + self.interface.send_data((end_x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] for each - start and end + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; + self.interface.send_data(start_y as u8)?; + self.interface.send_data((start_y >> 8) as u8)?; + self.interface.send_data(end_y as u8)?; + self.interface.send_data((end_y >> 8) as u8) + } + + pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; + self.interface.send_data((x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; + self.interface.send_data(y as u8)?; + self.interface.send_data((y >> 8) as u8)?; + + self.wait_until_idle(); + Ok(()) + } + + /// Uses the slower full update + pub fn set_lut(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_FULL_UPDATE) + } + + /// Uses the quick partial refresh + pub fn set_lut_quick(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_PARTIAL_UPDATE) + } + + //TODO: assert length for LUT is exactly 30 + fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { + self.set_lut_helper(buffer) + } + + + fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { + assert!(buffer.len() == 30); + self.interface.send_command(Command::WRITE_LUT_REGISTER)?; + self.interface.send_multiple_data(buffer) + } + +} \ No newline at end of file diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index e9d1f00..ff927f0 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -1,2 +1,308 @@ -pub(crate) const WIDTH: u16 = 128; -pub(crate) const HEIGHT: u16 = 296; \ No newline at end of file +//! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI +//! +//! +//! # Examples from the 4.2" Display. It should work the same for the 2.9" one. +//! +//! ```ignore +//! 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(); +//! ``` + +const WIDTH: u16 = 128; +const HEIGHT: u16 = 296; + +use epds::EPD; + + +use hal::{ + blocking::{ + spi::Write, + delay::* + }, + digital::* +}; + + +use type_a::luts::*; +pub use type_a::command::Command; + +use drawing::color::Color; + + + + +use interface::*; + +use interface::connection_interface::ConnectionInterface; + + + + +/// EPD2in9 driver +/// +pub struct EPD2in9 { + /// SPI + interface: ConnectionInterface, + /// EPD (width, height) + //epd: EPD, + /// Color + background_color: Color, +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs +{ + +} + + +impl WaveshareInterface + for EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs, +{ + + fn get_width(&self) -> u16 { + WIDTH + } + + fn get_height(&self) -> u16 { + HEIGHT + } + + + fn new( + interface: ConnectionInterface + ) -> Result { + //let epd = EPD::new(WIDTH, HEIGHT); + let background_color = Color::White; + + let mut epd = EPD2in9 {interface, /*epd,*/ background_color}; + + + epd.init()?; + + Ok(epd) + } + + + + fn init(&mut self) -> Result<(), E> { + + + self.reset(); + + // 3 Databytes: + // A[7:0] + // 0.. A[8] + // 0.. B[2:0] + // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) + self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; + self.interface.send_data(HEIGHT as u8)?; + self.interface.send_data((HEIGHT >> 8) as u8)?; + self.interface.send_data(0x00)?; + + // 3 Databytes: (and default values from datasheet and arduino) + // 1 .. A[6:0] = 0xCF | 0xD7 + // 1 .. B[6:0] = 0xCE | 0xD6 + // 1 .. C[6:0] = 0x8D | 0x9D + //TODO: test + self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; + self.interface.send_data(0xD7)?; + self.interface.send_data(0xD6)?; + self.interface.send_data(0x9D)?; + + // One Databyte with value 0xA8 for 7V VCOM + self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; + self.interface.send_data(0xA8)?; + + // One Databyte with default value 0x1A for 4 dummy lines per gate + self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; + self.interface.send_data(0x1A)?; + + // One Databyte with default value 0x08 for 2us per line + self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; + self.interface.send_data(0x08)?; + + // One Databyte with default value 0x03 + // -> address: x increment, y increment, address counter is updated in x direction + self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; + self.interface.send_data(0x03)?; + + self.set_lut() + } + + fn sleep(&mut self) -> Result<(), E> { + + 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.wait_until_idle(); + Ok(()) + } + + + fn reset(&mut self) { + self.interface.reset() + } + + fn delay_ms(&mut self, delay: u16) { + self.interface.delay_ms(delay) + } + + + + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.use_full_frame()?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + //TODO: update description: last 3 bits will be ignored for width and x_pos + fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ + self.set_ram_area(x, y, x + width, y + height)?; + self.set_ram_counter(x, y)?; + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_multiple_data(buffer) + } + + + fn display_frame(&mut self) -> Result<(), E>{ + // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) + //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) + self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; + self.interface.send_data(0xC4)?; + + self.interface.send_command(Command::MASTER_ACTIVATION)?; + // MASTER Activation should not be interupted to avoid currption of panel images + // therefore a terminate command is send + self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) + } + + + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ + self.update_frame(buffer)?; + self.display_frame() + } + + + fn clear_frame(&mut self) -> Result<(), E>{ + self.use_full_frame()?; + + // clear the ram with the background color + let color = self.background_color.get_byte_value(); + + self.interface.send_command(Command::WRITE_RAM)?; + self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + } + + /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) + fn set_background_color(&mut self, background_color: Color){ + self.background_color = background_color; + } + +} + +impl EPD2in9 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + D: DelayUs + DelayMs, +{ + fn wait_until_idle(&mut self) { + self.interface.wait_until_idle(false); + } + + pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { + // choose full frame/ram + self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; + + // start from the beginning + self.set_ram_counter(0,0) + } + + pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { + assert!(start_x < end_x); + assert!(start_y < end_y); + + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; + self.interface.send_data((start_x >> 3) as u8)?; + self.interface.send_data((end_x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] for each - start and end + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; + self.interface.send_data(start_y as u8)?; + self.interface.send_data((start_y >> 8) as u8)?; + self.interface.send_data(end_y as u8)?; + self.interface.send_data((end_y >> 8) as u8) + } + + pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { + // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram + // aren't relevant + self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; + self.interface.send_data((x >> 3) as u8)?; + + // 2 Databytes: A[7:0] & 0..A[8] + self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; + self.interface.send_data(y as u8)?; + self.interface.send_data((y >> 8) as u8)?; + + self.wait_until_idle(); + Ok(()) + } + + /// Uses the slower full update + pub fn set_lut(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_FULL_UPDATE) + } + + /// Uses the quick partial refresh + pub fn set_lut_quick(&mut self) -> Result<(), E> { + self.set_lut_helper(&LUT_PARTIAL_UPDATE) + } + + //TODO: assert length for LUT is exactly 30 + fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { + self.set_lut_helper(buffer) + } + + + fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { + assert!(buffer.len() == 30); + self.interface.send_command(Command::WRITE_LUT_REGISTER)?; + self.interface.send_multiple_data(buffer) + } + +} \ No newline at end of file diff --git a/src/epd4in2/constants.rs b/src/epd4in2/constants.rs index 190181f..cc866cf 100644 --- a/src/epd4in2/constants.rs +++ b/src/epd4in2/constants.rs @@ -1,5 +1,5 @@ -pub(crate) const WIDTH: usize = 400; -pub(crate) const HEIGHT: usize = 300; +pub(crate) const WIDTH: u16 = 400; +pub(crate) const HEIGHT: u16 = 300; pub(crate) const LUT_VCOM0: [u8; 44] = [ 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 7b2d49c..38afa60 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -49,7 +49,6 @@ use hal::{ blocking::{delay::*, spi::Write}, digital::*, - spi::{Mode, Phase, Polarity}, }; use interface::{connection_interface::ConnectionInterface, WaveshareInterface}; @@ -65,6 +64,14 @@ pub use self::command::Command; use epds::EPD; +pub(crate) fn new() -> EPD { + EPD::new( + constants::WIDTH, + constants::HEIGHT + ) +} + + /// EPD4in2 driver /// pub struct EPD4in2 @@ -81,10 +88,10 @@ pub struct EPD4in2 -impl WaveshareInterface +impl WaveshareInterface for EPD4in2 where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, DataCommand: OutputPin, @@ -114,7 +121,7 @@ where /// /// epd4in2.sleep(); /// ``` - fn new(interface: ConnectionInterface, epd: EPD) -> Result { + fn new(interface: ConnectionInterface) -> Result { let width = WIDTH as u16; let height = HEIGHT as u16; @@ -132,7 +139,7 @@ where Ok(epd) } - fn init(&mut self) -> Result<(), SPI_Error> { + fn init(&mut self) -> Result<(), SpiError> { // reset the device self.reset(); @@ -179,7 +186,7 @@ where Ok(()) } - fn sleep(&mut self) -> Result<(), SPI_Error> { + fn sleep(&mut self) -> Result<(), SpiError> { 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 @@ -208,7 +215,7 @@ where self.interface.delay_ms(delay) } - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SPI_Error> { + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError> { let color_value = self.color.get_byte_value(); self.send_resolution()?; @@ -244,7 +251,7 @@ where y: u16, width: u16, height: u16, - ) -> Result<(), SPI_Error> { + ) -> Result<(), SpiError> { if buffer.len() as u16 != width / 8 * height { //TODO: panic!! or sth like that @@ -281,13 +288,13 @@ where self.send_command(Command::PARTIAL_OUT) } - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), SPI_Error>{ + fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError>{ self.update_frame(buffer)?; self.display_frame() } - fn display_frame(&mut self) -> Result<(), SPI_Error> { + fn display_frame(&mut self) -> Result<(), SpiError> { self.send_command(Command::DISPLAY_REFRESH)?; self.wait_until_idle(); @@ -297,7 +304,7 @@ where // TODO: add this abstraction function // fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>; - fn clear_frame(&mut self) -> Result<(), SPI_Error> { + fn clear_frame(&mut self) -> Result<(), SpiError> { self.send_resolution()?; let size = self.width / 8 * self.height; @@ -325,24 +332,24 @@ where } } -impl EPD4in2 +impl EPD4in2 where - SPI: Write, + SPI: Write, CS: OutputPin, BUSY: InputPin, DC: OutputPin, RST: OutputPin, D: DelayUs + DelayMs, { - fn send_command(&mut self, command: Command) -> Result<(), SPI_Error> { + fn send_command(&mut self, command: Command) -> Result<(), SpiError> { self.interface.send_command(command) } - fn send_data(&mut self, val: u8) -> Result<(), SPI_Error> { + fn send_data(&mut self, val: u8) -> Result<(), SpiError> { self.interface.send_data(val) } - fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SPI_Error> { + fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SpiError> { self.interface.send_multiple_data(data) } @@ -350,7 +357,7 @@ where self.interface.wait_until_idle(true) } - fn send_resolution(&mut self) -> Result<(), SPI_Error> { + fn send_resolution(&mut self) -> Result<(), SpiError> { let w = self.get_width(); let h = self.get_height(); @@ -363,7 +370,7 @@ where /// Fill the look-up table for the EPD //TODO: make public? - fn set_lut(&mut self) -> Result<(), SPI_Error> { + fn set_lut(&mut self) -> Result<(), SpiError> { self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) } @@ -372,7 +379,7 @@ where /// Is automatically done by [EPD4in2::display_frame_quick()](EPD4in2::display_frame_quick()) /// //TODO: make public? #[cfg(feature = "epd4in2_fast_update")] - fn set_lut_quick(&mut self) -> Result<(), SPI_Error> { + fn set_lut_quick(&mut self) -> Result<(), SpiError> { self.set_lut_helper( &LUT_VCOM0_QUICK, &LUT_WW_QUICK, @@ -389,7 +396,7 @@ where lut_bw: &[u8], lut_wb: &[u8], lut_bb: &[u8], - ) -> Result<(), SPI_Error> { + ) -> Result<(), SpiError> { // LUT VCOM self.send_command(Command::LUT_FOR_VCOM)?; self.send_multiple_data(lut_vcom)?; diff --git a/src/epds.rs b/src/epds.rs index d348ddb..c1ed3f8 100644 --- a/src/epds.rs +++ b/src/epds.rs @@ -1,4 +1,3 @@ - /// A struct containing necessary info about a epd (electronic paper display). E.g: /// /// - Width @@ -6,7 +5,7 @@ /// ... /// /// This needs to be implemented by each new Display -pub struct EPD { +pub(crate) struct EPD { pub(crate) width: u16, pub(crate) height: u16 //displayrotation? @@ -16,6 +15,4 @@ impl EPD { pub(crate) fn new(width: u16, height: u16) -> EPD { EPD {width, height} } - - } \ No newline at end of file diff --git a/src/interface/connection_interface.rs b/src/interface/connection_interface.rs index 4a4cad3..361937a 100644 --- a/src/interface/connection_interface.rs +++ b/src/interface/connection_interface.rs @@ -10,7 +10,7 @@ use interface::Command; /// The Connection Interface of all (?) Waveshare EPD-Devices /// -pub(crate) struct ConnectionInterface { +pub struct ConnectionInterface { /// SPI spi: SPI, /// CS for SPI @@ -35,7 +35,7 @@ where RST: OutputPin, Delay: DelayUs + DelayMs, { - pub(crate) fn new(spi: SPI, cs: CS, busy: BUSY, dc: DataCommand, rst: RST, delay: Delay) -> Self { + pub fn new(spi: SPI, cs: CS, busy: BUSY, dc: DataCommand, rst: RST, delay: Delay) -> Self { ConnectionInterface {spi, cs, busy, dc, rst, delay } } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 308ced9..9469644 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -7,7 +7,6 @@ use hal::{ }; use core::marker::Sized; - use drawing::color::Color; /// Interface for the physical connection between display and the controlling device @@ -28,6 +27,8 @@ pub trait Displays { } + + //TODO: add LUT trait with set_fast_lut and set_manual_lut and set_normal_lut or sth like that? // for partial updates trait LUTSupport { @@ -56,7 +57,7 @@ pub trait WaveshareInterface /// /// This already initialises the device. That means [init()](WaveshareInterface::init()) isn't needed directly afterwards fn new( - interface: ConnectionInterface, + interface: ConnectionInterface ) -> Result where Self: Sized; diff --git a/src/lib.rs b/src/lib.rs index 28493fc..ed70e1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ use hal::{ pub mod drawing; pub mod epd4in2; - +mod epds; pub mod epd2in9; @@ -62,7 +62,6 @@ pub mod interface; pub mod type_a; - //TODO: test spi mode /// SPI mode - /// For more infos see [Requirements: SPI](index.html#spi) diff --git a/src/type_a/command.rs b/src/type_a/command.rs index 884adc8..8b61db7 100644 --- a/src/type_a/command.rs +++ b/src/type_a/command.rs @@ -91,8 +91,8 @@ mod tests { fn command_addr() { assert_eq!(Command::DRIVER_OUTPUT_CONTROL.address(), 0x01); - assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.addr(), 0x4E); + assert_eq!(Command::SET_RAM_X_ADDRESS_COUNTER.address(), 0x4E); - assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.addr(), 0xFF); + assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.address(), 0xFF); } } \ No newline at end of file diff --git a/src/type_a/constants.rs b/src/type_a/luts.rs similarity index 100% rename from src/type_a/constants.rs rename to src/type_a/luts.rs diff --git a/src/type_a/mod.rs b/src/type_a/mod.rs index 5f66f99..54ddbeb 100644 --- a/src/type_a/mod.rs +++ b/src/type_a/mod.rs @@ -28,8 +28,8 @@ use hal::{ digital::* }; -mod constants; -use self::constants::*; +pub(crate) mod luts; +use self::luts::*; use drawing::color::Color; @@ -40,17 +40,18 @@ use interface::*; use interface::connection_interface::ConnectionInterface; +use epds::EPD; +pub(crate) const WIDTH: u16 = 128; +pub(crate) const HEIGHT: u16 = 296; /// EPD2in9 driver /// pub struct EPD2in9 { /// SPI interface: ConnectionInterface, - /// Width - width: u16, - /// Height - height: u16, + /// EPD (width, height) + epd: EPD, /// Color background_color: Color, } @@ -80,24 +81,21 @@ where { fn get_width(&self) -> u16 { - self.width + self.epd.width } fn get_height(&self) -> u16 { - self.height + self.epd.height } fn new( - interface: ConnectionInterface, - display: Displays - ) -> Result { - let width = WIDTH as u16; - let height = HEIGHT as u16; - + interface: ConnectionInterface + ) -> Result { + let epd = EPD::new(WIDTH, HEIGHT); let background_color = Color::White; - let mut epd = EPD2in9 {interface, width, height, background_color}; + let mut epd = EPD2in9 {interface, epd, background_color}; epd.init()?; @@ -118,8 +116,8 @@ where // 0.. B[2:0] // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; - self.interface.send_data(HEIGHT as u8)?; - self.interface.send_data((HEIGHT >> 8) as u8)?; + self.interface.send_data(self.epd.height as u8)?; + self.interface.send_data((self.epd.height >> 8) as u8)?; self.interface.send_data(0x00)?; // 3 Databytes: (and default values from datasheet and arduino) @@ -217,7 +215,7 @@ where let color = self.background_color.get_byte_value(); self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_data_x_times(color, WIDTH / 8 * HEIGHT) + self.interface.send_data_x_times(color, self.epd.width / 8 * self.epd.height) } /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) @@ -241,8 +239,11 @@ where } pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { + let width = self.epd.width; + let height = self.epd.height; + // choose full frame/ram - self.set_ram_area(0, 0, WIDTH - 1, HEIGHT - 1)?; + self.set_ram_area(0, 0, width - 1, height - 1)?; // start from the beginning self.set_ram_counter(0,0) From 5dbf873631e0cfb2244312d6569845aaac1f8263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 13:54:23 +0200 Subject: [PATCH 09/22] Moved type_a LUTs to type_a mod.rs , updated dependencies accordingly --- src/epd1in54/mod.rs | 18 ++++++++++-------- src/epd2in9/mod.rs | 7 +++++-- src/type_a/luts.rs | 14 -------------- 3 files changed, 15 insertions(+), 24 deletions(-) delete mode 100644 src/type_a/luts.rs diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index a9aab64..5a0f47f 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -35,9 +35,11 @@ use hal::{ digital::* }; - -use type_a::luts::*; -pub use type_a::command::Command; +use type_a::{ + LUT_FULL_UPDATE, + LUT_PARTIAL_UPDATE, + command::Command +}; use drawing::color::Color; @@ -53,7 +55,7 @@ use interface::connection_interface::ConnectionInterface; /// EPD2in9 driver /// -pub struct EPD2in9 { +pub struct EPD1in54 { /// SPI interface: ConnectionInterface, /// EPD (width, height) @@ -62,7 +64,7 @@ pub struct EPD2in9 { background_color: Color, } -impl EPD2in9 +impl EPD1in54 where SPI: Write, CS: OutputPin, @@ -76,7 +78,7 @@ where impl WaveshareInterface - for EPD2in9 + for EPD1in54 where SPI: Write, CS: OutputPin, @@ -101,7 +103,7 @@ where let epd = EPD::new(WIDTH, HEIGHT); let background_color = Color::White; - let mut epd = EPD2in9 {interface, /*epd,*/ background_color}; + let mut epd = EPD1in54 {interface, /*epd,*/ background_color}; epd.init()?; @@ -231,7 +233,7 @@ where } -impl EPD2in9 +impl EPD1in54 where SPI: Write, CS: OutputPin, diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index ff927f0..60a7b88 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -34,8 +34,11 @@ use hal::{ }; -use type_a::luts::*; -pub use type_a::command::Command; +use type_a::{ + LUT_FULL_UPDATE, + LUT_PARTIAL_UPDATE, + command::Command +}; use drawing::color::Color; diff --git a/src/type_a/luts.rs b/src/type_a/luts.rs deleted file mode 100644 index 63de296..0000000 --- a/src/type_a/luts.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Original Waveforms from Waveshare -pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[ - 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, - 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, - 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, - 0x35, 0x51, 0x51, 0x19, 0x01, 0x00 -]; - -pub(crate) const LUT_PARTIAL_UPDATE: [u8; 30] =[ - 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -]; \ No newline at end of file From 94c30dd1eb7ebe160f688fd6583656dfdde4725c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:50:26 +0200 Subject: [PATCH 10/22] updated travis to test all features --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 037000f..077438d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,8 +98,8 @@ script: - cargo build - cargo build --release - cargo test - - cargo test --release - - cargo doc --release + - cargo test --all-features --release + - cargo doc --all-features --release - cd examples/embedded_linux && cargo build && cd ../../ #- cd ../f3_stm32f30x && cargo build From bd4efa4f6baac6359a62ee77d0f54e060fbfbd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:51:18 +0200 Subject: [PATCH 11/22] added feature gates for the different displays --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6535ebd..0c59493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,11 @@ authors = ["Christoph Groß "] [features] -default = [] +default = ["epd1in54", "epd2in9", "epd4in2"] + +epd1in54 = [] +epd2in9 = [] +epd4in2 = [] # Activates the fast LUT for EPD4in2 epd4in2_fast_update = [] From 6ca5d58919b3ac87c0c82c62e7debb80ca3d931d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:51:31 +0200 Subject: [PATCH 12/22] still a feature gate update --- src/lib.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ed70e1f..2872c47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,14 +51,29 @@ use hal::{ }; pub mod drawing; -pub mod epd4in2; -mod epds; +mod interface; +pub use interface::{WaveshareInterface}; -pub mod epd2in9; +#[cfg(feature="epd4in2")] +mod epd4in2; +#[cfg(feature="epd4in2")] +pub use epd4in2::EPD4in2; -pub mod interface; +#[cfg(feature="epd1in54")] +mod epd1in54; +#[cfg(feature="epd1in54")] +pub use epd1in54::EPD1in54; + +#[cfg(feature="epd2in9")] +mod epd2in9; +///2in9 eink +#[cfg(feature="epd2in9")] +///2in9 eink +pub use epd2in9::EPD2in9; + +#[cfg(any(feature="epd1in54", feature="epd2in9"))] pub mod type_a; From e439153862f3360010beff6f6ad02d1b79e20570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:51:50 +0200 Subject: [PATCH 13/22] Epds isn't really needed --- src/epds.rs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/epds.rs diff --git a/src/epds.rs b/src/epds.rs deleted file mode 100644 index c1ed3f8..0000000 --- a/src/epds.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// A struct containing necessary info about a epd (electronic paper display). E.g: -/// -/// - Width -/// - Height -/// ... -/// -/// This needs to be implemented by each new Display -pub(crate) struct EPD { - pub(crate) width: u16, - pub(crate) height: u16 - //displayrotation? -} - -impl EPD { - pub(crate) fn new(width: u16, height: u16) -> EPD { - EPD {width, height} - } -} \ No newline at end of file From a08b285cf7fb7e24635f93eb8205480097785ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:52:23 +0200 Subject: [PATCH 14/22] removed display trait --- src/interface/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 9469644..31f9ff7 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -21,12 +21,6 @@ pub(crate) trait Command { } -pub trait Displays { - fn width(self) -> u8; - fn height(self) -> u8; -} - - //TODO: add LUT trait with set_fast_lut and set_manual_lut and set_normal_lut or sth like that? From 121a61b72d02e644620365a452e00bce1189ffee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:52:42 +0200 Subject: [PATCH 15/22] removed the unused epd stuff --- src/epd4in2/mod.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 38afa60..6cf40a2 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -62,15 +62,6 @@ use drawing::color::Color; pub mod command; pub use self::command::Command; -use epds::EPD; - -pub(crate) fn new() -> EPD { - EPD::new( - constants::WIDTH, - constants::HEIGHT - ) -} - /// EPD4in2 driver /// From 5c8972ec46aaaac8e4ffa4743abf25f1dbaa1a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:54:20 +0200 Subject: [PATCH 16/22] small epd removel update --- src/epd1in54/mod.rs | 10 ++-------- src/epd2in9/mod.rs | 8 +++----- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index 5a0f47f..19b394a 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -22,10 +22,7 @@ const WIDTH: u16 = 200; const HEIGHT: u16 = 200; const DPI: u16 = 184; - - -use epds::EPD; - +const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ blocking::{ @@ -100,11 +97,8 @@ where fn new( interface: ConnectionInterface ) -> Result { - let epd = EPD::new(WIDTH, HEIGHT); - let background_color = Color::White; - - let mut epd = EPD1in54 {interface, /*epd,*/ background_color}; + let mut epd = EPD1in54 {interface, background_color: DEFAULT_BACKGROUND_COLOR}; epd.init()?; diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 60a7b88..646ba25 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -21,9 +21,7 @@ const WIDTH: u16 = 128; const HEIGHT: u16 = 296; - -use epds::EPD; - +const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ blocking::{ @@ -100,9 +98,9 @@ where interface: ConnectionInterface ) -> Result { //let epd = EPD::new(WIDTH, HEIGHT); - let background_color = Color::White; + //let background_color = Color::White; - let mut epd = EPD2in9 {interface, /*epd,*/ background_color}; + let mut epd = EPD2in9 {interface, background_color: DEFAULT_BACKGROUND_COLOR}; epd.init()?; From 864626f8fd8ec3f99b5e719eec216d063d0352ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 14:55:02 +0200 Subject: [PATCH 17/22] removed all now uneccessary stuff from type_a as it's back to the direct display module (epd1in54 and epd2in9) --- src/type_a/mod.rs | 318 ++-------------------------------------------- 1 file changed, 14 insertions(+), 304 deletions(-) diff --git a/src/type_a/mod.rs b/src/type_a/mod.rs index 54ddbeb..4ddcc58 100644 --- a/src/type_a/mod.rs +++ b/src/type_a/mod.rs @@ -1,307 +1,17 @@ -//! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI -//! -//! -//! # Examples from the 4.2" Display. It should work the same for the 2.9" one. -//! -//! ```ignore -//! 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(); -//! ``` - - -use hal::{ - blocking::{ - spi::Write, - delay::* - }, - digital::* -}; - -pub(crate) mod luts; -use self::luts::*; - -use drawing::color::Color; - pub mod command; pub use self::command::Command; -use interface::*; - -use interface::connection_interface::ConnectionInterface; - -use epds::EPD; - - -pub(crate) const WIDTH: u16 = 128; -pub(crate) const HEIGHT: u16 = 296; -/// EPD2in9 driver -/// -pub struct EPD2in9 { - /// SPI - interface: ConnectionInterface, - /// EPD (width, height) - epd: EPD, - /// Color - background_color: Color, -} - -impl EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DataCommand: OutputPin, - RST: OutputPin, - Delay: DelayUs + DelayMs -{ - -} - - -impl WaveshareInterface - for EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DataCommand: OutputPin, - RST: OutputPin, - Delay: DelayUs + DelayMs, -{ - - fn get_width(&self) -> u16 { - self.epd.width - } - - fn get_height(&self) -> u16 { - self.epd.height - } - - - fn new( - interface: ConnectionInterface - ) -> Result { - let epd = EPD::new(WIDTH, HEIGHT); - let background_color = Color::White; - - let mut epd = EPD2in9 {interface, epd, background_color}; - - - epd.init()?; - - Ok(epd) - } - - - - fn init(&mut self) -> Result<(), E> { - - - self.reset(); - - // 3 Databytes: - // A[7:0] - // 0.. A[8] - // 0.. B[2:0] - // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?) - self.interface.send_command(Command::DRIVER_OUTPUT_CONTROL)?; - self.interface.send_data(self.epd.height as u8)?; - self.interface.send_data((self.epd.height >> 8) as u8)?; - self.interface.send_data(0x00)?; - - // 3 Databytes: (and default values from datasheet and arduino) - // 1 .. A[6:0] = 0xCF | 0xD7 - // 1 .. B[6:0] = 0xCE | 0xD6 - // 1 .. C[6:0] = 0x8D | 0x9D - //TODO: test - self.interface.send_command(Command::BOOSTER_SOFT_START_CONTROL)?; - self.interface.send_data(0xD7)?; - self.interface.send_data(0xD6)?; - self.interface.send_data(0x9D)?; - - // One Databyte with value 0xA8 for 7V VCOM - self.interface.send_command(Command::WRITE_VCOM_REGISTER)?; - self.interface.send_data(0xA8)?; - - // One Databyte with default value 0x1A for 4 dummy lines per gate - self.interface.send_command(Command::SET_DUMMY_LINE_PERIOD)?; - self.interface.send_data(0x1A)?; - - // One Databyte with default value 0x08 for 2us per line - self.interface.send_command(Command::SET_GATE_LINE_WIDTH)?; - self.interface.send_data(0x08)?; - - // One Databyte with default value 0x03 - // -> address: x increment, y increment, address counter is updated in x direction - self.interface.send_command(Command::DATA_ENTRY_MODE_SETTING)?; - self.interface.send_data(0x03)?; - - self.set_lut() - } - - fn sleep(&mut self) -> Result<(), E> { - - 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.wait_until_idle(); - Ok(()) - } - - - fn reset(&mut self) { - self.interface.reset() - } - - fn delay_ms(&mut self, delay: u16) { - self.interface.delay_ms(delay) - } - - - - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.use_full_frame()?; - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) - } - - //TODO: update description: last 3 bits will be ignored for width and x_pos - fn update_partial_frame(&mut self, buffer: &[u8], x: u16, y: u16, width: u16, height: u16) -> Result<(), E>{ - self.set_ram_area(x, y, x + width, y + height)?; - self.set_ram_counter(x, y)?; - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_multiple_data(buffer) - } - - - fn display_frame(&mut self) -> Result<(), E>{ - // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version) - //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet) - self.interface.send_command(Command::DISPLAY_UPDATE_CONTROL_2)?; - self.interface.send_data(0xC4)?; - - self.interface.send_command(Command::MASTER_ACTIVATION)?; - // MASTER Activation should not be interupted to avoid currption of panel images - // therefore a terminate command is send - self.interface.send_command(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE) - } - - - fn update_and_display_frame(&mut self, buffer: &[u8]) -> Result<(), E>{ - self.update_frame(buffer)?; - self.display_frame() - } - - - fn clear_frame(&mut self) -> Result<(), E>{ - self.use_full_frame()?; - - // clear the ram with the background color - let color = self.background_color.get_byte_value(); - - self.interface.send_command(Command::WRITE_RAM)?; - self.interface.send_data_x_times(color, self.epd.width / 8 * self.epd.height) - } - - /// Sets the backgroundcolor for various commands like [WaveshareInterface::clear_frame()](clear_frame()) - fn set_background_color(&mut self, background_color: Color){ - self.background_color = background_color; - } - -} - -impl EPD2in9 -where - SPI: Write, - CS: OutputPin, - BUSY: InputPin, - DC: OutputPin, - RST: OutputPin, - D: DelayUs + DelayMs, -{ - fn wait_until_idle(&mut self) { - self.interface.wait_until_idle(false); - } - - pub(crate) fn use_full_frame(&mut self) -> Result<(), E> { - let width = self.epd.width; - let height = self.epd.height; - - // choose full frame/ram - self.set_ram_area(0, 0, width - 1, height - 1)?; - - // start from the beginning - self.set_ram_counter(0,0) - } - - pub(crate) fn set_ram_area(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) -> Result<(), E> { - assert!(start_x < end_x); - assert!(start_y < end_y); - - // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_START_END_POSITION)?; - self.interface.send_data((start_x >> 3) as u8)?; - self.interface.send_data((end_x >> 3) as u8)?; - - // 2 Databytes: A[7:0] & 0..A[8] for each - start and end - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_START_END_POSITION)?; - self.interface.send_data(start_y as u8)?; - self.interface.send_data((start_y >> 8) as u8)?; - self.interface.send_data(end_y as u8)?; - self.interface.send_data((end_y >> 8) as u8) - } - - pub(crate) fn set_ram_counter(&mut self, x: u16, y: u16) -> Result<(), E> { - // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram - // aren't relevant - self.interface.send_command(Command::SET_RAM_X_ADDRESS_COUNTER)?; - self.interface.send_data((x >> 3) as u8)?; - - // 2 Databytes: A[7:0] & 0..A[8] - self.interface.send_command(Command::SET_RAM_Y_ADDRESS_COUNTER)?; - self.interface.send_data(y as u8)?; - self.interface.send_data((y >> 8) as u8)?; - - self.wait_until_idle(); - Ok(()) - } - - /// Uses the slower full update - pub fn set_lut(&mut self) -> Result<(), E> { - self.set_lut_helper(&LUT_FULL_UPDATE) - } - - /// Uses the quick partial refresh - pub fn set_lut_quick(&mut self) -> Result<(), E> { - self.set_lut_helper(&LUT_PARTIAL_UPDATE) - } - - //TODO: assert length for LUT is exactly 30 - fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { - self.set_lut_helper(buffer) - } - - - fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { - assert!(buffer.len() == 30); - self.interface.send_command(Command::WRITE_LUT_REGISTER)?; - self.interface.send_multiple_data(buffer) - } - -} \ No newline at end of file +// Original Waveforms from Waveshare +pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[ + 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, + 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, + 0x35, 0x51, 0x51, 0x19, 0x01, 0x00 +]; + +pub(crate) const LUT_PARTIAL_UPDATE: [u8; 30] =[ + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +]; From 4870b72336b2030ff41b0c5f305e2abade15bc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 15:10:51 +0200 Subject: [PATCH 18/22] reduced visibility of commands as they normally should only be needed interally --- src/epd4in2/command.rs | 2 +- src/epd4in2/mod.rs | 2 +- src/type_a/command.rs | 2 +- src/type_a/mod.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/epd4in2/command.rs b/src/epd4in2/command.rs index e22787c..c41b951 100644 --- a/src/epd4in2/command.rs +++ b/src/epd4in2/command.rs @@ -10,7 +10,7 @@ use interface; #[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] -pub enum Command { +pub(crate) enum Command { /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift direction, booster switch, soft reset PANEL_SETTING = 0x00, /// selecting internal and external power diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 6cf40a2..e3f5b78 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -60,7 +60,7 @@ use self::constants::*; use drawing::color::Color; pub mod command; -pub use self::command::Command; +use self::command::Command; /// EPD4in2 driver diff --git a/src/type_a/command.rs b/src/type_a/command.rs index 8b61db7..f467a12 100644 --- a/src/type_a/command.rs +++ b/src/type_a/command.rs @@ -11,7 +11,7 @@ use interface; //#[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] -pub enum Command { +pub(crate) enum Command { /// Driver Output control /// 3 Databytes: /// A[7:0] diff --git a/src/type_a/mod.rs b/src/type_a/mod.rs index 4ddcc58..987a032 100644 --- a/src/type_a/mod.rs +++ b/src/type_a/mod.rs @@ -1,5 +1,4 @@ -pub mod command; -pub use self::command::Command; +pub(crate) mod command; // Original Waveforms from Waveshare pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[ From 835e25d1338452f9c99142a887c3d6f1d3d7380c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Mon, 6 Aug 2018 15:23:21 +0200 Subject: [PATCH 19/22] Used cargo clippy to fix a few bad coding styles --- src/drawing/color.rs | 14 ++++++++------ src/drawing/mod.rs | 14 ++++++-------- src/epd1in54/mod.rs | 8 ++++---- src/epd2in9/mod.rs | 6 +++--- src/type_a/command.rs | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/drawing/color.rs b/src/drawing/color.rs index a7e2354..1ee507d 100644 --- a/src/drawing/color.rs +++ b/src/drawing/color.rs @@ -29,9 +29,10 @@ impl Color { /// remember: 1 is white, 0 is black /// Color is the color you want to draw with in the foreground pub(crate) fn get_color(input: u8, pos: u8, color: &Color) -> Color { - match Color::is_drawable_pixel(input, pos) { - true => Color::normal_color(color), - false => Color::inverse_color(color) + if Color::is_drawable_pixel(input, pos) { + Color::normal_color(color) + } else { + Color::inverse_color(color) } } @@ -65,9 +66,10 @@ impl Color { // - black for pixel to draw // //foreground color is the color you want to have in the foreground - match Color::is_drawable_pixel(input, pos) { - true => Color::normal_color(foreground_color), - false => Color::inverse_color(foreground_color) + if Color::is_drawable_pixel(input, pos) { + Color::normal_color(foreground_color) + } else { + Color::inverse_color(foreground_color) } } } \ No newline at end of file diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs index 60487be..08763cd 100644 --- a/src/drawing/mod.rs +++ b/src/drawing/mod.rs @@ -141,7 +141,7 @@ impl<'a> Graphics<'a> { let mut counter = 0; for input_char in input.chars() { self.draw_char(x0 + counter, y0, input_char, font, color); - counter += font.get_char_width(input_char) as u16; + counter += u16::from(font.get_char_width(input_char)); } } @@ -163,7 +163,7 @@ impl<'a> Graphics<'a> { for _ in 0..8 { self.draw_pixel( - x0 + width_counter as u16, + x0 + u16::from(width_counter), y0 + row_counter, &Color::get_color(elem, width_counter % 8, color)); @@ -183,9 +183,9 @@ impl<'a> Graphics<'a> { pub fn draw_char_8x8(&mut self, x0: u16, y0: u16, input: char, color: &Color) { let mut counter = 0; // includes special draw_char instructions as this one is ordered columnwise and not rowwise (first byte == first 8 pixel columnwise) - for &elem in font::bitmap_8x8(input).iter() { + for &elem in (&font::bitmap_8x8(input)).iter() { for i in 0..8u8 { - self.draw_pixel(x0 + counter, y0 + 7 - i as u16, &Color::convert_color(elem, i, color)) + self.draw_pixel(x0 + counter, y0 + 7 - u16::from(i), &Color::convert_color(elem, i, color)) } counter += 1; } @@ -197,10 +197,8 @@ impl<'a> Graphics<'a> { /// /// no autobreak line yet pub fn draw_string_8x8(&mut self, x0: u16, y0: u16, input: &str, color: &Color) { - let mut counter = 0; - for input_char in input.chars() { - self.draw_char_8x8(x0 + counter*8, y0, input_char, color); - counter += 1; + for (counter, input_char) in input.chars().enumerate() { + self.draw_char_8x8(x0 + counter as u16*8, y0, input_char, color); } } diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index 19b394a..45dc595 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -21,7 +21,7 @@ const WIDTH: u16 = 200; const HEIGHT: u16 = 200; -const DPI: u16 = 184; +//const DPI: u16 = 184; const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ @@ -292,9 +292,9 @@ where } //TODO: assert length for LUT is exactly 30 - fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { - self.set_lut_helper(buffer) - } + //fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { + // self.set_lut_helper(buffer) + //} fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 646ba25..d6e74e4 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -295,9 +295,9 @@ where } //TODO: assert length for LUT is exactly 30 - fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { - self.set_lut_helper(buffer) - } + //fn set_lut_manual(&mut self, buffer: &[u8]) -> Result<(), E> { + // self.set_lut_helper(buffer) + //} fn set_lut_helper(&mut self, buffer: &[u8]) -> Result<(), E> { diff --git a/src/type_a/command.rs b/src/type_a/command.rs index f467a12..4adf5a7 100644 --- a/src/type_a/command.rs +++ b/src/type_a/command.rs @@ -8,7 +8,7 @@ use interface; /// Should rarely (never?) be needed directly. /// /// For more infos about the addresses and what they are doing look into the pdfs -//#[allow(dead_code)] +#[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] pub(crate) enum Command { From 49bad9c5472cc0daadf388c93bd54c2234debede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Tue, 7 Aug 2018 09:17:45 +0200 Subject: [PATCH 20/22] fixed the wrong reference to the epd2in9 display instead of the 1in54 --- src/epd1in54/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index 45dc595..ccaf7c5 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -50,7 +50,7 @@ use interface::connection_interface::ConnectionInterface; -/// EPD2in9 driver +/// EPD1in54 driver /// pub struct EPD1in54 { /// SPI From 7758e37e1a3bbd5adfffbb49e687384b5adb8496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Tue, 7 Aug 2018 09:19:50 +0200 Subject: [PATCH 21/22] - Made Connectioninterface public in root module, - fixed the example (which needed updated dependencies) --- examples/embedded_linux/src/main.rs | 9 ++++----- src/lib.rs | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/embedded_linux/src/main.rs b/examples/embedded_linux/src/main.rs index 9716463..0b9d169 100644 --- a/examples/embedded_linux/src/main.rs +++ b/examples/embedded_linux/src/main.rs @@ -6,11 +6,10 @@ extern crate eink_waveshare_rs; use eink_waveshare_rs::{ - epd4in2::{EPD4in2, self}, + EPD4in2, drawing::{Graphics, color::Color}, - interface::{ - WaveshareInterface, - connection_interface::ConnectionInterface}, + WaveshareInterface, + ConnectionInterface }; use lin_hal::spidev::{self, SpidevOptions}; @@ -106,7 +105,7 @@ fn main() { //TODO: wait for Digital::InputPin //fixed currently with the HackInputPin, see further above let connection_interface = ConnectionInterface::new(spi, cs, busy_in, dc, rst, delay); - let mut epd4in2 = EPD4in2::new(connection_interface, epd4in2::new()).expect("eink inialize error"); + let mut epd4in2 = EPD4in2::new(connection_interface).expect("eink inialize error"); //let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; let mut buffer = [0u8; 15000]; diff --git a/src/lib.rs b/src/lib.rs index 2872c47..814f104 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,9 @@ use hal::{ pub mod drawing; mod interface; -pub use interface::{WaveshareInterface}; +pub use interface::{ + WaveshareInterface, + connection_interface::ConnectionInterface}; #[cfg(feature="epd4in2")] mod epd4in2; From 23be76a848a476024fc171fafa80ca67b31ba87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Gro=C3=9F?= Date: Tue, 7 Aug 2018 10:04:23 +0200 Subject: [PATCH 22/22] Renamed example to embedded_linux_epd4in2 instead of embedded_linux to make the display which is used in this example more explicit --- .travis.yml | 2 +- examples/{embedded_linux => embedded_linux_epd4in2}/Cargo.toml | 0 examples/{embedded_linux => embedded_linux_epd4in2}/src/main.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename examples/{embedded_linux => embedded_linux_epd4in2}/Cargo.toml (100%) rename examples/{embedded_linux => embedded_linux_epd4in2}/src/main.rs (100%) diff --git a/.travis.yml b/.travis.yml index 077438d..9140d58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -100,7 +100,7 @@ script: - cargo test - cargo test --all-features --release - cargo doc --all-features --release - - cd examples/embedded_linux && cargo build && cd ../../ + - cd examples/embedded_linux_epd4in2 && cargo build && cd ../../ #- cd ../f3_stm32f30x && cargo build after_success: diff --git a/examples/embedded_linux/Cargo.toml b/examples/embedded_linux_epd4in2/Cargo.toml similarity index 100% rename from examples/embedded_linux/Cargo.toml rename to examples/embedded_linux_epd4in2/Cargo.toml diff --git a/examples/embedded_linux/src/main.rs b/examples/embedded_linux_epd4in2/src/main.rs similarity index 100% rename from examples/embedded_linux/src/main.rs rename to examples/embedded_linux_epd4in2/src/main.rs