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] 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)