diff --git a/.travis.yml b/.travis.yml index 037000f..9140d58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,9 +98,9 @@ script: - cargo build - cargo build --release - cargo test - - cargo test --release - - cargo doc --release - - cd examples/embedded_linux && cargo build && cd ../../ + - cargo test --all-features --release + - cargo doc --all-features --release + - cd examples/embedded_linux_epd4in2 && cargo build && cd ../../ #- cd ../f3_stm32f30x && cargo build after_success: 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 = [] diff --git a/README.md b/README.md index 3910839..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 | ✕ | ✔ | ✔ | | @@ -34,6 +36,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) 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 94% rename from examples/embedded_linux/src/main.rs rename to examples/embedded_linux_epd4in2/src/main.rs index ced4d9d..0b9d169 100644 --- a/examples/embedded_linux/src/main.rs +++ b/examples/embedded_linux_epd4in2/src/main.rs @@ -5,7 +5,12 @@ 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, + drawing::{Graphics, color::Color}, + WaveshareInterface, + ConnectionInterface +}; use lin_hal::spidev::{self, SpidevOptions}; use lin_hal::{Pin, Spidev}; @@ -99,7 +104,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).expect("eink inialize error"); //let mut buffer = [0u8, epd4in2.get_width() / 8 * epd4in2.get_height()]; let mut buffer = [0u8; 15000]; 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 new file mode 100644 index 0000000..ccaf7c5 --- /dev/null +++ b/src/epd1in54/mod.rs @@ -0,0 +1,306 @@ +//! 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; +const DEFAULT_BACKGROUND_COLOR: Color = Color::White; + +use hal::{ + blocking::{ + spi::Write, + delay::* + }, + digital::* +}; + +use type_a::{ + LUT_FULL_UPDATE, + LUT_PARTIAL_UPDATE, + command::Command +}; + +use drawing::color::Color; + + + + +use interface::*; + +use interface::connection_interface::ConnectionInterface; + + + + +/// EPD1in54 driver +/// +pub struct EPD1in54 { + /// SPI + interface: ConnectionInterface, + /// EPD (width, height) + //epd: EPD, + /// Color + background_color: Color, +} + +impl EPD1in54 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DataCommand: OutputPin, + RST: OutputPin, + Delay: DelayUs + DelayMs +{ + +} + + +impl WaveshareInterface + for EPD1in54 +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 mut epd = EPD1in54 {interface, background_color: DEFAULT_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 EPD1in54 +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 6b0a7cd..d6e74e4 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -19,6 +19,9 @@ //! epd4in2.sleep(); //! ``` +const WIDTH: u16 = 128; +const HEIGHT: u16 = 296; +const DEFAULT_BACKGROUND_COLOR: Color = Color::White; use hal::{ blocking::{ @@ -28,13 +31,17 @@ use hal::{ digital::* }; -mod constants; -use self::constants::*; + +use type_a::{ + LUT_FULL_UPDATE, + LUT_PARTIAL_UPDATE, + command::Command +}; use drawing::color::Color; -pub mod command; -pub use self::command::Command; + + use interface::*; @@ -42,67 +49,58 @@ use interface::connection_interface::ConnectionInterface; + /// EPD2in9 driver /// -pub struct EPD2in9 { +pub struct EPD2in9 { /// SPI - interface: ConnectionInterface, - /// Width - width: u16, - /// Height - height: u16, + interface: ConnectionInterface, + /// EPD (width, height) + //epd: EPD, /// Color background_color: Color, } -impl EPD2in9 +impl EPD2in9 where SPI: Write, CS: OutputPin, BUSY: InputPin, - DC: OutputPin, + DataCommand: OutputPin, RST: OutputPin, - D: DelayUs + DelayMs + Delay: DelayUs + DelayMs { } -impl WaveshareInterface for EPD2in9 +impl WaveshareInterface + for EPD2in9 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 + WIDTH } fn get_height(&self) -> u16 { - self.height + 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; + interface: ConnectionInterface + ) -> Result { + //let epd = EPD::new(WIDTH, HEIGHT); + //let background_color = Color::White; - let interface = ConnectionInterface::new(spi, cs, busy, dc, rst, delay); - - let background_color = Color::White; - - let mut epd = EPD2in9 {interface, width, height, background_color}; + let mut epd = EPD2in9 {interface, background_color: DEFAULT_BACKGROUND_COLOR}; epd.init()?; @@ -297,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/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/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 0f5ee76..e3f5b78 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}; @@ -61,19 +60,13 @@ use self::constants::*; 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 self::command::Command; + /// EPD4in2 driver /// -pub struct EPD4in2 { +pub struct EPD4in2 + { /// Connection Interface interface: ConnectionInterface, /// Width @@ -84,15 +77,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 +112,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 +130,7 @@ where Ok(epd) } - fn init(&mut self) -> Result<(), E> { + fn init(&mut self) -> Result<(), SpiError> { // reset the device self.reset(); @@ -182,7 +177,7 @@ where Ok(()) } - fn sleep(&mut self) -> Result<(), E> { + 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 @@ -211,7 +206,7 @@ where self.interface.delay_ms(delay) } - fn update_frame(&mut self, buffer: &[u8]) -> Result<(), E> { + fn update_frame(&mut self, buffer: &[u8]) -> Result<(), SpiError> { let color_value = self.color.get_byte_value(); self.send_resolution()?; @@ -247,7 +242,8 @@ where y: u16, width: u16, height: u16, - ) -> Result<(), E> { + ) -> Result<(), SpiError> { + if buffer.len() as u16 != width / 8 * height { //TODO: panic!! or sth like that //return Err("Wrong buffersize"); @@ -283,13 +279,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<(), SpiError>{ self.update_frame(buffer)?; self.display_frame() } - fn display_frame(&mut self) -> Result<(), E> { + fn display_frame(&mut self) -> Result<(), SpiError> { self.send_command(Command::DISPLAY_REFRESH)?; self.wait_until_idle(); @@ -299,7 +295,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<(), SpiError> { self.send_resolution()?; let size = self.width / 8 * self.height; @@ -327,24 +323,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<(), SpiError> { self.interface.send_command(command) } - fn send_data(&mut self, val: u8) -> Result<(), E> { + fn send_data(&mut self, val: u8) -> Result<(), SpiError> { self.interface.send_data(val) } - fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), E> { + fn send_multiple_data(&mut self, data: &[u8]) -> Result<(), SpiError> { self.interface.send_multiple_data(data) } @@ -352,7 +348,7 @@ where self.interface.wait_until_idle(true) } - fn send_resolution(&mut self) -> Result<(), E> { + fn send_resolution(&mut self) -> Result<(), SpiError> { let w = self.get_width(); let h = self.get_height(); @@ -365,7 +361,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<(), SpiError> { self.set_lut_helper(&LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) } @@ -374,7 +370,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<(), SpiError> { self.set_lut_helper( &LUT_VCOM0_QUICK, &LUT_WW_QUICK, @@ -391,7 +387,7 @@ where lut_bw: &[u8], lut_wb: &[u8], lut_bb: &[u8], - ) -> Result<(), E> { + ) -> Result<(), SpiError> { // LUT VCOM self.send_command(Command::LUT_FOR_VCOM)?; self.send_multiple_data(lut_vcom)?; diff --git a/src/interface/connection_interface.rs b/src/interface/connection_interface.rs index 7ae2cc5..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 @@ -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 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) { diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 0bf783a..31f9ff7 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -7,11 +7,11 @@ use hal::{ }; use core::marker::Sized; - 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 +21,8 @@ pub(crate) trait Command { } + + //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 +32,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 +51,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 +62,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 +78,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 +101,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. /// diff --git a/src/lib.rs b/src/lib.rs index a6558f8..814f104 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,14 +51,32 @@ use hal::{ }; pub mod drawing; -pub mod epd4in2; +mod interface; +pub use interface::{ + WaveshareInterface, + connection_interface::ConnectionInterface}; +#[cfg(feature="epd4in2")] +mod epd4in2; +#[cfg(feature="epd4in2")] +pub use epd4in2::EPD4in2; -pub mod epd2in9; +#[cfg(feature="epd1in54")] +mod epd1in54; +#[cfg(feature="epd1in54")] +pub use epd1in54::EPD1in54; -pub mod interface; +#[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; //TODO: test spi mode diff --git a/src/epd2in9/command.rs b/src/type_a/command.rs similarity index 84% rename from src/epd2in9/command.rs rename to src/type_a/command.rs index 016257b..4adf5a7 100644 --- a/src/epd2in9/command.rs +++ b/src/type_a/command.rs @@ -1,19 +1,17 @@ -//! 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(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] @@ -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.address(), 0x4E); - //assert_eq!(Command::DISPLAY_REFRESH.addr(), 0x12); + assert_eq!(Command::TERMINATE_COMMANDS_AND_FRAME_WRITE.address(), 0xFF); } } \ No newline at end of file diff --git a/src/epd2in9/constants.rs b/src/type_a/mod.rs similarity index 88% rename from src/epd2in9/constants.rs rename to src/type_a/mod.rs index 98e7b05..987a032 100644 --- a/src/epd2in9/constants.rs +++ b/src/type_a/mod.rs @@ -1,5 +1,4 @@ -pub(crate) const WIDTH: u16 = 128; -pub(crate) const HEIGHT: u16 = 296; +pub(crate) mod command; // Original Waveforms from Waveshare pub(crate) const LUT_FULL_UPDATE: [u8; 30] =[ @@ -14,4 +13,4 @@ pub(crate) const LUT_PARTIAL_UPDATE: [u8; 30] =[ 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 +];