From 9b20bae1008804d15ab9aca6c6569aa2ad2abe4f Mon Sep 17 00:00:00 2001 From: Denis Chaplygin Date: Wed, 17 Nov 2021 20:05:29 +0200 Subject: [PATCH] Added support for 5.83" Black/Red/White Display --- README.md | 1 + src/epd2in13bc/graphics.rs | 4 +- src/epd5in83b_v2/command.rs | 134 ++++++++++++++ src/epd5in83b_v2/graphics.rs | 189 +++++++++++++++++++ src/epd5in83b_v2/mod.rs | 342 +++++++++++++++++++++++++++++++++++ src/graphics.rs | 52 +++++- src/lib.rs | 1 + 7 files changed, 713 insertions(+), 10 deletions(-) create mode 100644 src/epd5in83b_v2/command.rs create mode 100644 src/epd5in83b_v2/graphics.rs create mode 100644 src/epd5in83b_v2/mod.rs diff --git a/README.md b/README.md index ce37369..5965813 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ epd.update_and_display_frame( & mut spi, & display.buffer()) ?; | [7.5 Inch B/W HD (A)](https://www.waveshare.com/product/displays/e-paper/epaper-1/7.5inch-hd-e-paper-hat.htm) | Black, White | ✕ | ✕ | ✔ | ✔ | | [7.5 Inch B/W V2 (A)](https://www.waveshare.com/product/7.5inch-e-paper-hat.htm) [[1](#1-75-inch-bw-v2-a)] | Black, White | ✕ | ✕ | ✔ | ✔ | | [7.5 Inch B/W (A)](https://www.waveshare.com/product/7.5inch-e-paper-hat.htm) | Black, White | ✕ | ✕ | ✔ | ✔ | +| [5.83 Inch B/W/R (b)](https://www.waveshare.com/5.83inch-e-Paper-B.htm) | Black, White, Red | ✕ | Not officially | ✔ | ✔ | | [4.2 Inch B/W (A)](https://www.waveshare.com/product/4.2inch-e-paper-module.htm) | Black, White | ✕ | Not officially [[2](#2-42-inch-e-ink-blackwhite---partial-refresh)] | ✔ | ✔ | | [1.54 Inch B/W (A)](https://www.waveshare.com/1.54inch-e-Paper-Module.htm) | Black, White | ✕ | ✔ | ✔ | ✔ | | [2.13 Inch B/W (A) V2](https://www.waveshare.com/product/2.13inch-e-paper-hat.htm) | Black, White | ✕ | ✔ | ✔ | ✔ | diff --git a/src/epd2in13bc/graphics.rs b/src/epd2in13bc/graphics.rs index 9d29a40..ffe8bbe 100644 --- a/src/epd2in13bc/graphics.rs +++ b/src/epd2in13bc/graphics.rs @@ -1,6 +1,6 @@ use crate::color::TriColor; use crate::epd2in13bc::{DEFAULT_BACKGROUND_COLOR, HEIGHT, NUM_DISPLAY_BITS, WIDTH}; -use crate::graphics::{DisplayRotation, TriDisplay}; +use crate::graphics::{DisplayColorRendering, DisplayRotation, TriDisplay}; use embedded_graphics_core::prelude::*; /// Full size buffer for use with the 2.13" b/c EPD @@ -31,7 +31,7 @@ impl DrawTarget for Display2in13bc { I: IntoIterator>, { for pixel in pixels { - self.draw_helper_tri(WIDTH, HEIGHT, pixel)?; + self.draw_helper_tri(WIDTH, HEIGHT, pixel, DisplayColorRendering::Positive)?; } Ok(()) } diff --git a/src/epd5in83b_v2/command.rs b/src/epd5in83b_v2/command.rs new file mode 100644 index 0000000..46b6b64 --- /dev/null +++ b/src/epd5in83b_v2/command.rs @@ -0,0 +1,134 @@ +//! SPI Commands for the Waveshare 5.83" E-Ink Display + +use crate::traits; + +/// Epd5in83 commands +/// +/// Should rarely (never?) be needed directly. +/// +/// For more infos about the addresses and what they are doing look into the PDFs. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift + /// direction, booster switch, soft reset. + PanelSetting = 0x00, + + /// Selecting internal and external power + PowerSetting = 0x01, + + /// After the Power Off command, the driver will power off following the Power Off + /// Sequence; BUSY signal will become "0". This command will turn off charge pump, + /// T-con, source driver, gate driver, VCOM, and temperature sensor, but register + /// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain + /// as previous condition, which may have 2 conditions: 0V or floating. + PowerOff = 0x02, + + /// Setting Power OFF sequence + PowerOffSequenceSetting = 0x03, + + /// Turning On the Power + /// + /// After the Power ON command, the driver will power on following the Power ON + /// sequence. Once complete, the BUSY signal will become "1". + PowerOn = 0x04, + + /// Starting data transmission + BoosterSoftStart = 0x06, + + /// This command makes the chip enter the deep-sleep mode to save power. + /// + /// The deep sleep mode would return to stand-by by hardware reset. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + DeepSleep = 0x07, + + /// This command starts transmitting B/W data and write them into SRAM. To complete data + /// transmission, commands Display Refresh or Data Start Transmission2 must be issued. Then the chip will start to + /// send data/VCOM for panel. + DataStartTransmission1 = 0x10, + + /// This command starts transmitting RED data and write them into SRAM. To complete data + /// transmission, command Display refresh must be issued. Then the chip will start to + /// send data/VCOM for panel. + DataStartTransmission2 = 0x13, + + /// To stop data transmission, this command must be issued to check the `data_flag`. + /// + /// After this command, BUSY signal will become "0" until the display update is + /// finished. + DataStop = 0x11, + + /// After this command is issued, driver will refresh display (data/VCOM) according to + /// SRAM data and LUT. + /// + /// After Display Refresh command, BUSY signal will become "0" until the display + /// update is finished. + DisplayRefresh = 0x12, + + /// Enables or disables Dual SPI mode + DualSPI = 0x15, + + /// The command controls the PLL clock frequency. + PllControl = 0x30, + + /// This command reads the temperature sensed by the temperature sensor. + TemperatureSensorCalibration = 0x40, + /// This command selects the Internal or External temperature sensor. + TemperatureSensorSelection = 0x41, + /// This command could write data to the external temperature sensor. + TemperatureSensorWrite = 0x42, + /// This command could read data from the external temperature sensor. + TemperatureSensorRead = 0x43, + + /// This command indicates the interval of Vcom and data output. When setting the + /// vertical back porch, the total blanking will be kept (20 Hsync). + VcomAndDataIntervalSetting = 0x50, + /// This command indicates the input power condition. Host can read this flag to learn + /// the battery condition. + LowPowerDetection = 0x51, + + /// This command defines non-overlap period of Gate and Source. + TconSetting = 0x60, + /// This command defines alternative resolution and this setting is of higher priority + /// than the RES\[1:0\] in R00H (PSR). + TconResolution = 0x61, + + /// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000. + Revision = 0x70, + /// This command reads the IC status. + GetStatus = 0x71, + + /// This command implements related VCOM sensing setting. + AutoMeasurementVcom = 0x80, + /// This command gets the VCOM value. + ReadVcomValue = 0x81, + /// This command sets `VCOM_DC` value. + VcmDcSetting = 0x82, + + /// Sets window size for the partial update + PartialWindow = 0x90, + /// Sets chip into partial update mode + PartialIn = 0x91, + /// Quits partial update mode + PartialOut = 0x92, +} + +impl traits::Command for Command { + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::Command as CommandTrait; + + #[test] + fn command_addr() { + assert_eq!(Command::PanelSetting.address(), 0x00); + assert_eq!(Command::DisplayRefresh.address(), 0x12); + } +} diff --git a/src/epd5in83b_v2/graphics.rs b/src/epd5in83b_v2/graphics.rs new file mode 100644 index 0000000..798a7b2 --- /dev/null +++ b/src/epd5in83b_v2/graphics.rs @@ -0,0 +1,189 @@ +use crate::color::TriColor; +use crate::epd5in83b_v2::{DEFAULT_BACKGROUND_COLOR, HEIGHT, NUM_DISPLAY_BITS, WIDTH}; +use crate::graphics::{DisplayColorRendering, DisplayRotation}; +use crate::prelude::TriDisplay; +use embedded_graphics_core::prelude::*; + +/// Full size buffer for use with the 5in83 EPD +/// +/// Can also be manually constructed: +/// `buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 2 * NUM_DISPLAY_BITS as usize]` +pub struct Display5in83 { + buffer: [u8; 2 * NUM_DISPLAY_BITS as usize], + rotation: DisplayRotation, +} + +impl Default for Display5in83 { + fn default() -> Self { + let mut display = Display5in83 { + buffer: [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 2 * NUM_DISPLAY_BITS as usize], + rotation: DisplayRotation::default(), + }; + // We need to invert chromatic part to black so it will be render white + let offset = display.chromatic_offset(); + display.buffer[offset..].fill(0x00); + display + } +} + +impl DrawTarget for Display5in83 { + type Color = TriColor; + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + self.draw_helper_tri(WIDTH, HEIGHT, pixel, DisplayColorRendering::Negative)?; + } + Ok(()) + } +} + +impl OriginDimensions for Display5in83 { + fn size(&self) -> Size { + Size::new(WIDTH, HEIGHT) + } +} + +impl TriDisplay for Display5in83 { + fn buffer(&self) -> &[u8] { + &self.buffer + } + + fn get_mut_buffer(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + fn rotation(&self) -> DisplayRotation { + self.rotation + } + + fn chromatic_offset(&self) -> usize { + NUM_DISPLAY_BITS as usize + } + + fn bw_buffer(&self) -> &[u8] { + &self.buffer[0..self.chromatic_offset()] + } + + fn chromatic_buffer(&self) -> &[u8] { + &self.buffer[self.chromatic_offset()..] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::color::Black; + use crate::color::Color; + use crate::graphics::{Display, DisplayRotation}; + use crate::{epd5in83b_v2, epd7in5}; + use embedded_graphics::{ + prelude::*, + primitives::{Line, PrimitiveStyle}, + }; + + // test buffer length + #[test] + fn graphics_size() { + let display = Display5in83::default(); + assert_eq!(display.buffer().len(), 38400); + } + + // test default background color on all bytes + #[test] + fn graphics_default() { + let display = Display5in83::default(); + for &byte in display.buffer() { + assert_eq!( + byte, + epd5in83b_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value() + ); + } + } + + #[test] + fn graphics_rotation_0() { + let mut display = Display5in83::default(); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!( + byte, + epd5in83b_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value() + ); + } + } + + #[test] + fn graphics_rotation_90() { + let mut display = Display5in83::default(); + display.set_rotation(DisplayRotation::Rotate90); + let _ = Line::new(Point::new(0, 632), Point::new(0, 639)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!( + byte, + epd5in83b_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value() + ); + } + } + + #[test] + fn graphics_rotation_180() { + let mut display = Display5in83::default(); + display.set_rotation(DisplayRotation::Rotate180); + let _ = Line::new(Point::new(632, 383), Point::new(639, 383)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!( + byte, + epd5in83b_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value() + ); + } + } + + #[test] + fn graphics_rotation_270() { + let mut display = Display5in83::default(); + display.set_rotation(DisplayRotation::Rotate270); + let _ = Line::new(Point::new(383, 0), Point::new(383, 7)) + .into_styled(PrimitiveStyle::with_stroke(Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!( + byte, + epd5in83b_v2::DEFAULT_BACKGROUND_COLOR.get_byte_value() + ); + } + } +} diff --git a/src/epd5in83b_v2/mod.rs b/src/epd5in83b_v2/mod.rs new file mode 100644 index 0000000..5664880 --- /dev/null +++ b/src/epd5in83b_v2/mod.rs @@ -0,0 +1,342 @@ +//! A simple Driver for the Waveshare 5.83" (B) v2 E-Ink Display via SPI +//! +//! # References +//! +//! - [Datasheet](https://www.waveshare.com/5.83inch-e-Paper-B.htm) +//! - [Waveshare C driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_5in83b_V2.c) +//! - [Waveshare Python driver](https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd5in83b_V2.py) + +use embedded_hal::{ + blocking::{delay::*, spi::Write}, + digital::v2::{InputPin, OutputPin}, +}; + +use crate::color::Color; +use crate::interface::DisplayInterface; +use crate::prelude::{WaveshareDisplay, WaveshareThreeColorDisplay}; +use crate::traits::{InternalWiAdditions, RefreshLut}; + +pub(crate) mod command; +use self::command::Command; + +#[cfg(feature = "graphics")] +mod graphics; +#[cfg(feature = "graphics")] +pub use self::graphics::Display5in83; + +/// Width of the display +pub const WIDTH: u32 = 648; +/// Height of the display +pub const HEIGHT: u32 = 480; +/// Default Background Color +pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +const IS_BUSY_LOW: bool = true; +const NUM_DISPLAY_BITS: u32 = WIDTH * HEIGHT / 8; + +/// Epd7in5 driver +/// +pub struct Epd5in83 { + /// Connection Interface + interface: DisplayInterface, + /// Background Color + color: Color, +} + +impl InternalWiAdditions + for Epd5in83 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayMs, +{ + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // Reset the device + self.interface.reset(delay, 10); + + // Start the booster + self.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x1e, 0x17])?; + + // Set the power settings: VGH=20V,VGL=-20V,VDH=15V,VDL=-15V + self.cmd_with_data(spi, Command::PowerSetting, &[0x07, 0x07, 0x3F, 0x3F])?; + + // Power on + self.command(spi, Command::PowerOn)?; + delay.delay_ms(5); + self.wait_until_idle(); + + // Set the panel settings: BWROTP + self.cmd_with_data(spi, Command::PanelSetting, &[0x0F])?; + + // Set the real resolution + self.send_resolution(spi)?; + + // Disable dual SPI + self.cmd_with_data(spi, Command::DualSPI, &[0x00])?; + + // Set Vcom and data interval + self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x11, 0x07])?; + + // Set S2G and G2S non-overlap periods to 12 (default) + self.cmd_with_data(spi, Command::TconSetting, &[0x22])?; + + self.wait_until_idle(); + Ok(()) + } +} + +impl WaveshareThreeColorDisplay + for Epd5in83 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayMs, +{ + fn update_color_frame( + &mut self, + spi: &mut SPI, + black: &[u8], + chromatic: &[u8], + ) -> Result<(), SPI::Error> { + self.update_achromatic_frame(spi, black)?; + self.update_chromatic_frame(spi, chromatic)?; + Ok(()) + } + + fn update_achromatic_frame(&mut self, spi: &mut SPI, black: &[u8]) -> Result<(), SPI::Error> { + self.wait_until_idle(); + self.cmd_with_data(spi, Command::DataStartTransmission1, black)?; + Ok(()) + } + + fn update_chromatic_frame( + &mut self, + spi: &mut SPI, + chromatic: &[u8], + ) -> Result<(), SPI::Error> { + self.wait_until_idle(); + self.cmd_with_data(spi, Command::DataStartTransmission2, chromatic)?; + Ok(()) + } +} + +impl WaveshareDisplay + for Epd5in83 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayMs, +{ + type DisplayColor = Color; + fn new( + spi: &mut SPI, + cs: CS, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + ) -> Result { + let interface = DisplayInterface::new(cs, busy, dc, rst); + let color = DEFAULT_BACKGROUND_COLOR; + + let mut epd = Epd5in83 { interface, color }; + + epd.init(spi, delay)?; + + Ok(epd) + } + + fn sleep(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(); + self.command(spi, Command::PowerOff)?; + self.wait_until_idle(); + self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?; + Ok(()) + } + + fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.init(spi, delay) + } + + fn set_background_color(&mut self, color: Color) { + self.color = color; + } + + fn background_color(&self) -> &Color { + &self.color + } + + fn width(&self) -> u32 { + WIDTH + } + + fn height(&self) -> u32 { + HEIGHT + } + + fn update_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + _delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(); + self.update_achromatic_frame(spi, buffer)?; + let color = self.color.get_byte_value(); + self.command(spi, Command::DataStartTransmission2)?; + self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?; + Ok(()) + } + + fn update_partial_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(); + if buffer.len() as u32 != width / 8 * height { + //TODO panic or error + } + + let hrst_upper = (x / 8) as u8 >> 6; + let hrst_lower = ((x / 8) << 3) as u8; + let hred_upper = ((x + width) / 8) as u8 >> 6; + let hred_lower = (((x + width) / 8) << 3) as u8 & 0b111; + let vrst_upper = (y >> 8) as u8; + let vrst_lower = y as u8; + let vred_upper = ((y + height) >> 8) as u8; + let vred_lower = (y + height) as u8; + let pt_scan = 0x01; // Gates scan both inside and outside of the partial window. (default) + + self.command(spi, Command::PartialIn)?; + self.command(spi, Command::PartialWindow)?; + self.send_data( + spi, + &[ + hrst_upper, hrst_lower, hred_upper, hred_lower, vrst_upper, vrst_lower, vred_upper, + vred_lower, pt_scan, + ], + )?; + self.command(spi, Command::DataStartTransmission1)?; + self.send_data(spi, buffer)?; + + let color = self.color.get_byte_value(); // Only black channel partial updates are supported ATM + self.command(spi, Command::DataStartTransmission2)?; + self.interface + .data_x_times(spi, color, width * height / 8)?; + + self.command(spi, Command::DisplayRefresh)?; + self.wait_until_idle(); + + self.command(spi, Command::PartialOut)?; + Ok(()) + } + + fn display_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { + self.command(spi, Command::DisplayRefresh)?; + self.wait_until_idle(); + Ok(()) + } + + fn update_and_display_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer, delay)?; + self.display_frame(spi, delay)?; + Ok(()) + } + + fn clear_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(); + + // The Waveshare controllers all implement clear using 0x33 + self.command(spi, Command::DataStartTransmission1)?; + self.interface.data_x_times(spi, 0xFF, NUM_DISPLAY_BITS)?; + + self.command(spi, Command::DataStartTransmission2)?; + self.interface.data_x_times(spi, 0x00, NUM_DISPLAY_BITS)?; + + Ok(()) + } + + fn set_lut( + &mut self, + _spi: &mut SPI, + _refresh_rate: Option, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn is_busy(&self) -> bool { + self.interface.is_busy(IS_BUSY_LOW) + } +} + +impl Epd5in83 +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayMs, +{ + fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> { + self.interface.cmd(spi, command) + } + + fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + self.interface.data(spi, data) + } + + fn cmd_with_data( + &mut self, + spi: &mut SPI, + command: Command, + data: &[u8], + ) -> Result<(), SPI::Error> { + self.interface.cmd_with_data(spi, command, data) + } + + fn wait_until_idle(&mut self) { + let _ = self.interface.wait_until_idle(IS_BUSY_LOW); + } + + fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let w = self.width(); + let h = self.height(); + + self.command(spi, Command::TconResolution)?; + self.send_data(spi, &[(w >> 8) as u8])?; + self.send_data(spi, &[w as u8])?; + self.send_data(spi, &[(h >> 8) as u8])?; + self.send_data(spi, &[h as u8]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn epd_size() { + assert_eq!(WIDTH, 648); + assert_eq!(HEIGHT, 480); + assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White); + } +} diff --git a/src/graphics.rs b/src/graphics.rs index e2d78e7..b966761 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -24,6 +24,15 @@ impl Default for DisplayRotation { } } +/// Display specific pixel output configuration +#[derive(Clone, Copy)] +pub enum DisplayColorRendering { + /// Positive: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic + Positive, + /// Negative: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black + Negative, +} + /// Necessary traits for all displays to implement for drawing /// /// Adds support for: @@ -129,6 +138,7 @@ pub trait TriDisplay: DrawTarget { width: u32, height: u32, pixel: Pixel, + rendering: DisplayColorRendering, ) -> Result<(), Self::Error> { let rotation = self.rotation(); @@ -149,20 +159,46 @@ pub trait TriDisplay: DrawTarget { TriColor::Black => { // clear bit in bw-buffer -> black buffer[index] &= !bit; - // set bit in chromatic-buffer -> white - buffer[index + offset] |= bit; + match rendering { + DisplayColorRendering::Positive => { + // set bit in chromatic-buffer -> white + buffer[index + offset] |= bit; + } + DisplayColorRendering::Negative => { + // clear bit in chromatic-buffer -> white + buffer[index + offset] &= !bit; + } + } } TriColor::White => { // set bit in bw-buffer -> white buffer[index] |= bit; - // set bit in chromatic-buffer -> white - buffer[index + offset] |= bit; + match rendering { + DisplayColorRendering::Positive => { + // set bit in chromatic-buffer -> white + buffer[index + offset] |= bit; + } + DisplayColorRendering::Negative => { + // clear bit in chromatic-buffer -> white + buffer[index + offset] &= !bit; + } + } } TriColor::Chromatic => { - // set bit in b/w buffer (white) - buffer[index] |= bit; - // clear bit in chromatic buffer -> chromatic - buffer[index + offset] &= !bit; + match rendering { + DisplayColorRendering::Positive => { + // set bit in b/w buffer (white) + buffer[index] |= bit; + // clear bit in chromatic buffer -> chromatic + buffer[index + offset] &= !bit; + } + DisplayColorRendering::Negative => { + // set bit in b/w buffer (white) + buffer[index] |= bit; + // set bit in chromatic-buffer -> chromatic + buffer[index + offset] |= bit; + } + } } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 2fe9c5f..13281f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ pub mod epd2in9_v2; pub mod epd2in9bc; pub mod epd4in2; pub mod epd5in65f; +pub mod epd5in83b_v2; pub mod epd7in5; pub mod epd7in5_hd; pub mod epd7in5_v2;