From b67641ceb45de121f65dea1bde83205ddcda9cf4 Mon Sep 17 00:00:00 2001 From: Nikolay Yurin Date: Wed, 25 Jan 2023 19:57:40 -0800 Subject: [PATCH 1/6] Add EPD 4in2bc support --- examples/epd4in2_variable_size.rs | 3 +- src/color.rs | 74 ++-- src/epd1in54/mod.rs | 2 +- src/epd1in54b/mod.rs | 2 +- src/epd1in54c/mod.rs | 2 +- src/epd2in13_v2/mod.rs | 2 +- src/epd2in13bc/mod.rs | 2 +- src/epd2in7b/mod.rs | 2 +- src/epd2in9/mod.rs | 2 +- src/epd2in9_v2/mod.rs | 2 +- src/epd2in9bc/mod.rs | 2 +- src/epd3in7/mod.rs | 2 +- src/epd4in2/mod.rs | 4 +- src/epd4in2bc/mod.rs | 667 ++++++++++++++++++++++++++++++ src/epd5in65f/mod.rs | 2 +- src/epd5in83b_v2/mod.rs | 2 +- src/epd7in5/mod.rs | 2 +- src/epd7in5_hd/mod.rs | 2 +- src/epd7in5_v2/mod.rs | 2 +- src/epd7in5b_v2/mod.rs | 2 +- src/graphics.rs | 115 ++++-- src/lib.rs | 3 +- src/traits.rs | 2 +- 23 files changed, 810 insertions(+), 90 deletions(-) create mode 100644 src/epd4in2bc/mod.rs diff --git a/examples/epd4in2_variable_size.rs b/examples/epd4in2_variable_size.rs index 978b21d6..bb8cdeef 100644 --- a/examples/epd4in2_variable_size.rs +++ b/examples/epd4in2_variable_size.rs @@ -69,7 +69,8 @@ fn main() -> Result<(), SPIError> { let (x, y, width, height) = (50, 50, 250, 250); let mut buffer = [epd4in2::DEFAULT_BACKGROUND_COLOR.get_byte_value(); 62500]; //250*250 - let mut display = VarDisplay::new(width, height, &mut buffer, false).unwrap(); + let mut display = + VarDisplay::new(width, height, &mut buffer, DisplayMode::BwrBitOff as u8).unwrap(); display.set_rotation(DisplayRotation::Rotate0); draw_text(&mut display, "Rotate 0!", 5, 50); diff --git a/src/color.rs b/src/color.rs index 0177923c..6a4c0abf 100644 --- a/src/color.rs +++ b/src/color.rs @@ -3,6 +3,7 @@ //! EPD representation of multicolor with separate buffers //! for each bit makes it hard to properly represent colors here +use crate::prelude::DisplayMode; #[cfg(feature = "graphics")] use embedded_graphics_core::pixelcolor::BinaryColor; #[cfg(feature = "graphics")] @@ -80,21 +81,21 @@ pub trait ColorType { /// Return the data used to set a pixel color /// - /// * bwrbit is used to tell the value of the unused bit when a chromatic - /// color is set (TriColor only as for now) + /// * MODE is used to tell the value of the unused bit when a chromatic + /// color is set (TriColor only as for now) /// * pos is the pixel position in the line, used to know which pixels must be set /// /// Return values are : /// * .0 is the mask used to exclude this pixel from the byte (eg: 0x7F in BiColor) /// * .1 are the bits used to set the color in the byte (eg: 0x80 in BiColor) /// this is u16 because we set 2 bytes in case of split buffer - fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16); + fn bitmask(&self, mode: DisplayMode, pos: u32) -> (u8, u16); } impl ColorType for Color { const BITS_PER_PIXEL_PER_BUFFER: usize = 1; const BUFFER_COUNT: usize = 1; - fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) { + fn bitmask(&self, _mode: DisplayMode, pos: u32) -> (u8, u16) { let bit = 0x80 >> (pos % 8); match self { Color::Black => (!bit, 0u16), @@ -106,27 +107,34 @@ impl ColorType for Color { impl ColorType for TriColor { const BITS_PER_PIXEL_PER_BUFFER: usize = 1; const BUFFER_COUNT: usize = 2; - fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16) { + + fn bitmask(&self, mode: DisplayMode, pos: u32) -> (u8, u16) { let bit = 0x80 >> (pos % 8); - match self { - TriColor::Black => (!bit, 0u16), - TriColor::White => (!bit, bit as u16), - TriColor::Chromatic => ( - !bit, - if bwrbit { - (bit as u16) << 8 - } else { - (bit as u16) << 8 | bit as u16 - }, - ), - } + let mask = match mode { + DisplayMode::BwrBitOnColorInverted => match self { + TriColor::Black => (bit as u16) << 8, + TriColor::White => (bit as u16) << 8 | bit as u16, + TriColor::Chromatic => 0u16, + }, + DisplayMode::BwrBitOn => match self { + TriColor::Black => 0u16, + TriColor::White => bit as u16, + TriColor::Chromatic => (bit as u16) << 8, + }, + DisplayMode::BwrBitOff => match self { + TriColor::Black => 0u16, + TriColor::White => bit as u16, + TriColor::Chromatic => (bit as u16) << 8 | bit as u16, + }, + }; + (!bit, mask) } } impl ColorType for OctColor { const BITS_PER_PIXEL_PER_BUFFER: usize = 4; const BUFFER_COUNT: usize = 1; - fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) { + fn bitmask(&self, _mode: DisplayMode, pos: u32) -> (u8, u16) { let mask = !(0xF0 >> ((pos % 2) * 4)); let bits = self.get_nibble() as u16; (mask, if pos % 2 == 1 { bits } else { bits << 4 }) @@ -329,16 +337,16 @@ impl From for Color { impl From for Color { fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self { use embedded_graphics_core::pixelcolor::RgbColor; - if rgb == RgbColor::BLACK { - Color::Black - } else if rgb == RgbColor::WHITE { - Color::White - } else { - // choose closest color - if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 { - Color::White - } else { - Color::Black + match rgb { + RgbColor::BLACK => Color::Black, + RgbColor::WHITE => Color::White, + _ => { + // choose closest color + if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 { + Color::White + } else { + Color::Black + } } } } @@ -465,13 +473,11 @@ impl From for TriColor { impl From for TriColor { fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self { use embedded_graphics_core::pixelcolor::RgbColor; - if rgb == RgbColor::BLACK { - TriColor::Black - } else if rgb == RgbColor::WHITE { - TriColor::White - } else { + match rgb { + RgbColor::BLACK => TriColor::Black, + RgbColor::WHITE => TriColor::White, // there is no good approximation here since we don't know which color is 'chromatic' - TriColor::Chromatic + _ => TriColor::Chromatic, } } } diff --git a/src/epd1in54/mod.rs b/src/epd1in54/mod.rs index b598693e..8da2e12b 100644 --- a/src/epd1in54/mod.rs +++ b/src/epd1in54/mod.rs @@ -73,7 +73,7 @@ use crate::interface::DisplayInterface; pub type Display1in54 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd1in54b/mod.rs b/src/epd1in54b/mod.rs index 08aae37c..0e072f62 100644 --- a/src/epd1in54b/mod.rs +++ b/src/epd1in54b/mod.rs @@ -32,7 +32,7 @@ use crate::buffer_len; pub type Display1in54b = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd1in54c/mod.rs b/src/epd1in54c/mod.rs index bbe27302..5d82e0dd 100644 --- a/src/epd1in54c/mod.rs +++ b/src/epd1in54c/mod.rs @@ -29,7 +29,7 @@ use crate::buffer_len; pub type Display1in54c = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in13_v2/mod.rs b/src/epd2in13_v2/mod.rs index 5d8d4e0b..ee7cba6e 100644 --- a/src/epd2in13_v2/mod.rs +++ b/src/epd2in13_v2/mod.rs @@ -50,7 +50,7 @@ compile_error!( pub type Display2in13 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in13bc/mod.rs b/src/epd2in13bc/mod.rs index d1256c4c..ec9bbd74 100644 --- a/src/epd2in13bc/mod.rs +++ b/src/epd2in13bc/mod.rs @@ -86,7 +86,7 @@ use crate::buffer_len; pub type Display2in13bc = crate::graphics::Display< WIDTH, HEIGHT, - true, + { crate::graphics::DisplayMode::BwrBitOn as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 2) }, TriColor, >; diff --git a/src/epd2in7b/mod.rs b/src/epd2in7b/mod.rs index 42f25f19..6d508a00 100644 --- a/src/epd2in7b/mod.rs +++ b/src/epd2in7b/mod.rs @@ -34,7 +34,7 @@ use crate::buffer_len; pub type Display2in7b = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in9/mod.rs b/src/epd2in9/mod.rs index 0ad3d771..00ee3951 100644 --- a/src/epd2in9/mod.rs +++ b/src/epd2in9/mod.rs @@ -69,7 +69,7 @@ use crate::interface::DisplayInterface; pub type Display2in9 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in9_v2/mod.rs b/src/epd2in9_v2/mod.rs index df466cba..5ec543d4 100644 --- a/src/epd2in9_v2/mod.rs +++ b/src/epd2in9_v2/mod.rs @@ -104,7 +104,7 @@ use crate::traits::QuickRefresh; pub type Display2in9 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in9bc/mod.rs b/src/epd2in9bc/mod.rs index 7a4ba92c..3d7d7394 100644 --- a/src/epd2in9bc/mod.rs +++ b/src/epd2in9bc/mod.rs @@ -90,7 +90,7 @@ use crate::buffer_len; pub type Display2in9bc = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd3in7/mod.rs b/src/epd3in7/mod.rs index c01c49fa..cef93c1d 100644 --- a/src/epd3in7/mod.rs +++ b/src/epd3in7/mod.rs @@ -37,7 +37,7 @@ const SINGLE_BYTE_WRITE: bool = true; pub type Display3in7 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd4in2/mod.rs b/src/epd4in2/mod.rs index 6de676e6..c9833b6c 100644 --- a/src/epd4in2/mod.rs +++ b/src/epd4in2/mod.rs @@ -55,7 +55,7 @@ use crate::interface::DisplayInterface; use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLut, WaveshareDisplay}; //The Lookup Tables for the Display -mod constants; +pub mod constants; use crate::epd4in2::constants::*; /// Width of the display @@ -78,7 +78,7 @@ use crate::buffer_len; pub type Display4in2 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd4in2bc/mod.rs b/src/epd4in2bc/mod.rs new file mode 100644 index 00000000..0c9fe126 --- /dev/null +++ b/src/epd4in2bc/mod.rs @@ -0,0 +1,667 @@ +//! A simple Driver for the Waveshare 4.2" E-Ink Display via SPI +//! +//! +//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module), +//! [Ben Krasnows partial Refresh tips](https://benkrasnow.blogspot.de/2017/10/fast-partial-refresh-on-42-e-paper.html) and +//! the driver documents in the `pdfs`-folder as orientation. +//! +//! # Examples +//! +//!```rust, no_run +//!# use embedded_hal_mock::*; +//!# fn main() -> Result<(), MockError> { +//!use embedded_graphics::{ +//! prelude::*, primitives::{Line, PrimitiveStyle}, +//!}; +//!use epd_waveshare::{epd4in2bc::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = pin::Mock::new(&expectations); +//!# let busy_in = pin::Mock::new(&expectations); +//!# let dc = pin::Mock::new(&expectations); +//!# let rst = pin::Mock::new(&expectations); +//!# let mut delay = delay::MockNoop::new(); +//! +//!// Setup EPD +//!let mut epd = Epd4in2bc::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay, None)?; +//! +//!// Use display graphics from embedded-graphics +//!let mut display = Display4in2bc::default(); +//! +//!// Use embedded graphics for drawing a line +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(TriColor::Chromatic, 1)) +//! .draw(&mut display); +//! +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer(), &mut delay)?; +//!epd.display_frame(&mut spi, &mut delay)?; +//! +//!// Set the EPD to sleep +//!epd.sleep(&mut spi, &mut delay)?; +//!# Ok(()) +//!# } +//!``` +//! +//! +//! +//! BE CAREFUL! The screen can get ghosting/burn-ins through the Partial Fast Update Drawing. + +use embedded_hal::{ + blocking::{delay::*, spi::Write}, + digital::v2::*, +}; + +use crate::interface::DisplayInterface; +use crate::traits::{ + InternalWiAdditions, QuickRefresh, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay, +}; + +use crate::color::TriColor; + +//The Lookup Tables for the Display +use crate::epd4in2::command::Command; +use crate::epd4in2::constants::*; + +use crate::epd4in2::HEIGHT; +use crate::epd4in2::WIDTH; +/// Default Background Color +pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White; +const IS_BUSY_LOW: bool = true; + +use crate::buffer_len; + +/// Full size buffer for use with the 4in2 EPD +#[cfg(feature = "graphics")] +pub type Display4in2bc = crate::graphics::Display< + WIDTH, + HEIGHT, + { crate::graphics::DisplayMode::BwrBitOnColorInverted as u8 }, + { buffer_len(WIDTH as usize, HEIGHT as usize * 2) }, + TriColor, +>; + +/// Epd4in2bc driver +/// +pub struct Epd4in2bc { + /// Connection Interface + interface: DisplayInterface, + /// Background Color + color: TriColor, + /// Refresh LUT + refresh: RefreshLut, +} + +impl InternalWiAdditions + for Epd4in2bc +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // reset the device + self.interface.reset(delay, 10_000, 10_000); + + // set the power settings + self.interface.cmd_with_data( + spi, + Command::PowerSetting, + &[0x03, 0x00, 0x2b, 0x2b, 0xff], + )?; + + // start the booster + self.interface + .cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?; + + // set the panel settings + self.cmd_with_data(spi, Command::PanelSetting, &[0x0F])?; + + // // Set Frequency, 200 Hz didn't work on my board + // // 150Hz and 171Hz wasn't tested yet + // // TODO: Test these other frequencies + // // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ DEFAULT: 3c 50Hz + self.cmd_with_data(spi, Command::PllControl, &[0x3C])?; + + self.send_resolution(spi)?; + + self.interface + .cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?; + + self.interface + .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x7f])?; + + self.set_lut(spi, delay, None)?; + + // power on + self.command(spi, Command::PowerOn)?; + delay.delay_us(5000); + + self.wait_until_idle(spi, delay)?; + Ok(()) + } +} + +impl WaveshareThreeColorDisplay + for Epd4in2bc +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + fn update_color_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + black: &[u8], + chromatic: &[u8], + ) -> Result<(), SPI::Error> { + self.update_achromatic_frame(spi, delay, black)?; + self.update_chromatic_frame(spi, delay, chromatic) + } + + /// Update only the black/white data of the display. + /// + /// Finish by calling `update_chromatic_frame`. + fn update_achromatic_frame( + &mut self, + spi: &mut SPI, + _delay: &mut DELAY, + black: &[u8], + ) -> Result<(), SPI::Error> { + self.interface.cmd(spi, Command::DataStartTransmission1)?; + self.interface.data(spi, black)?; + Ok(()) + } + + /// Update only chromatic data of the display. + /// + /// This data takes precedence over the black/white data. + fn update_chromatic_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + chromatic: &[u8], + ) -> Result<(), SPI::Error> { + self.interface.cmd(spi, Command::DataStartTransmission2)?; + self.interface.data(spi, chromatic)?; + + self.wait_until_idle(spi, delay)?; + Ok(()) + } +} + +impl WaveshareDisplay + for Epd4in2bc +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + type DisplayColor = TriColor; + + fn new( + spi: &mut SPI, + cs: CS, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + delay_us: Option, + ) -> Result { + let interface = DisplayInterface::new(cs, busy, dc, rst, delay_us); + + let mut epd = Epd4in2bc { + interface, + color: DEFAULT_BACKGROUND_COLOR, + refresh: RefreshLut::Quick, + }; + + epd.init(spi, delay)?; + + Ok(epd) + } + + fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.interface + .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x7f])?; //border floating + self.command(spi, Command::VcmDcSetting)?; // VCOM to 0V + self.command(spi, Command::PanelSetting)?; + + self.command(spi, Command::PowerSetting)?; //VG&VS to 0V fast + for _ in 0..4 { + self.send_data(spi, &[0x00])?; + } + + self.command(spi, Command::PowerOff)?; + self.wait_until_idle(spi, delay)?; + self.interface + .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: TriColor) { + self.color = color; + } + + fn background_color(&self) -> &TriColor { + &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(spi, delay)?; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + self.interface.data(spi, buffer)?; + + let color_value = self.color.get_byte_value(); + + self.interface + .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?; + + self.interface + .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + + self.command(spi, Command::DataStop)?; + + Ok(()) + } + + fn update_partial_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + if buffer.len() as u32 != width / 8 * height { + //TODO: panic!! or sth like that + // return Err("Wrong buffersize"); + } + + self.command(spi, Command::PartialIn)?; + self.command(spi, Command::PartialWindow)?; + + self.send_data(spi, &[(x >> 8) as u8])?; + self.send_data(spi, &[(x & 0xf8) as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored + self.send_data(spi, &[(((x & 0xf8) + width - 1) >> 8) as u8])?; + self.send_data(spi, &[(((x & 0xf8) + width - 1) | 0x07) as u8])?; + + self.send_data(spi, &[(y >> 8) as u8])?; + self.send_data(spi, &[(y & 0xff) as u8])?; + self.send_data(spi, &[((y + height - 1) >> 8) as u8])?; + self.send_data(spi, &[((y + height - 1) & 0xff) as u8])?; + + self.send_data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default) + + delay.delay_us(2000); + self.command(spi, Command::DataStartTransmission1)?; + self.send_data(spi, buffer)?; + + let color_value = self.color.get_bit_value(); + self.interface.cmd(spi, Command::DataStartTransmission2)?; + self.interface + .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?; + + delay.delay_us(2000); + self.command(spi, Command::PartialOut)?; + Ok(()) + } + + fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.set_lut(spi, delay, None)?; + self.command(spi, Command::DisplayRefresh)?; + delay.delay_us(100_000); + self.wait_until_idle(spi, delay)?; + 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.command(spi, Command::DisplayRefresh)?; + Ok(()) + } + + fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.send_resolution(spi)?; + + let color_value = self.color.get_bit_value(); + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + self.interface + .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?; + + self.interface.cmd(spi, Command::DataStartTransmission2)?; + self.interface + .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?; + Ok(()) + } + + fn set_lut( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + refresh_rate: Option, + ) -> Result<(), SPI::Error> { + if let Some(refresh_lut) = refresh_rate { + self.refresh = refresh_lut; + } + match self.refresh { + RefreshLut::Full => { + self.set_lut_helper(spi, delay, &LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB) + } + RefreshLut::Quick => self.set_lut_helper( + spi, + delay, + &LUT_VCOM0_QUICK, + &LUT_WW_QUICK, + &LUT_BW_QUICK, + &LUT_WB_QUICK, + &LUT_BB_QUICK, + ), + } + } + + fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface.wait_until_idle(delay, IS_BUSY_LOW); + Ok(()) + } +} + +impl Epd4in2bc +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + 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 send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let w = self.width(); + let h = self.height(); + + self.command(spi, Command::ResolutionSetting)?; + 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]) + } + + #[allow(clippy::too_many_arguments)] + fn set_lut_helper( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + lut_vcom: &[u8], + lut_ww: &[u8], + lut_bw: &[u8], + lut_wb: &[u8], + lut_bb: &[u8], + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + // LUT VCOM + self.cmd_with_data(spi, Command::LutForVcom, lut_vcom)?; + + // LUT WHITE to WHITE + self.cmd_with_data(spi, Command::LutWhiteToWhite, lut_ww)?; + + // LUT BLACK to WHITE + self.cmd_with_data(spi, Command::LutBlackToWhite, lut_bw)?; + + // LUT WHITE to BLACK + self.cmd_with_data(spi, Command::LutWhiteToBlack, lut_wb)?; + + // LUT BLACK to BLACK + self.cmd_with_data(spi, Command::LutBlackToBlack, lut_bb)?; + Ok(()) + } + + /// Helper function. Sets up the display to send pixel data to a custom + /// starting point. + pub fn shift_display( + &mut self, + spi: &mut SPI, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.send_data(spi, &[(x >> 8) as u8])?; + let tmp = x & 0xf8; + self.send_data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored + let tmp = tmp + width - 1; + self.send_data(spi, &[(tmp >> 8) as u8])?; + self.send_data(spi, &[(tmp | 0x07) as u8])?; + + self.send_data(spi, &[(y >> 8) as u8])?; + self.send_data(spi, &[y as u8])?; + + self.send_data(spi, &[((y + height - 1) >> 8) as u8])?; + self.send_data(spi, &[(y + height - 1) as u8])?; + + self.send_data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default) + + Ok(()) + } +} + +impl QuickRefresh + for Epd4in2bc +where + SPI: Write, + CS: OutputPin, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayUs, +{ + /// To be followed immediately after by `update_old_frame`. + fn update_old_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + + self.interface.data(spi, buffer)?; + + Ok(()) + } + + /// To be used immediately after `update_old_frame`. + fn update_new_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.send_resolution(spi)?; + + self.interface.cmd(spi, Command::DataStartTransmission2)?; + + self.interface.data(spi, buffer)?; + + Ok(()) + } + + /// This is a wrapper around `display_frame` for using this device as a true + /// `QuickRefresh` device. + fn display_new_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.display_frame(spi, delay) + } + + /// This is wrapper around `update_new_frame` and `display_frame` for using + /// this device as a true `QuickRefresh` device. + /// + /// To be used immediately after `update_old_frame`. + fn update_and_display_new_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.update_new_frame(spi, buffer, delay)?; + self.display_frame(spi, delay) + } + + fn update_partial_old_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + + if buffer.len() as u32 != width / 8 * height { + //TODO: panic!! or sth like that + //return Err("Wrong buffersize"); + } + + self.interface.cmd(spi, Command::PartialIn)?; + self.interface.cmd(spi, Command::PartialWindow)?; + + self.shift_display(spi, x, y, width, height)?; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + + self.interface.data(spi, buffer)?; + + Ok(()) + } + + /// Always call `update_partial_old_frame` before this, with buffer-updating code + /// between the calls. + fn update_partial_new_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + if buffer.len() as u32 != width / 8 * height { + //TODO: panic!! or sth like that + //return Err("Wrong buffersize"); + } + + self.shift_display(spi, x, y, width, height)?; + + self.interface.cmd(spi, Command::DataStartTransmission2)?; + + self.interface.data(spi, buffer)?; + + self.interface.cmd(spi, Command::PartialOut)?; + Ok(()) + } + + fn clear_partial_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.send_resolution(spi)?; + + let color_value = self.color.get_byte_value(); + + self.interface.cmd(spi, Command::PartialIn)?; + self.interface.cmd(spi, Command::PartialWindow)?; + + self.shift_display(spi, x, y, width, height)?; + + self.interface.cmd(spi, Command::DataStartTransmission1)?; + self.interface + .data_x_times(spi, color_value, width / 8 * height)?; + + self.interface.cmd(spi, Command::DataStartTransmission2)?; + self.interface + .data_x_times(spi, color_value, width / 8 * height)?; + + self.interface.cmd(spi, Command::PartialOut)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn epd_size() { + assert_eq!(WIDTH, 400); + assert_eq!(HEIGHT, 300); + assert_eq!(DEFAULT_BACKGROUND_COLOR, TriColor::White); + } +} diff --git a/src/epd5in65f/mod.rs b/src/epd5in65f/mod.rs index 1a70b78a..d4727056 100644 --- a/src/epd5in65f/mod.rs +++ b/src/epd5in65f/mod.rs @@ -25,7 +25,7 @@ use crate::buffer_len; pub type Display5in65f = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 4) }, OctColor, >; diff --git a/src/epd5in83b_v2/mod.rs b/src/epd5in83b_v2/mod.rs index 625b1e7c..71683daf 100644 --- a/src/epd5in83b_v2/mod.rs +++ b/src/epd5in83b_v2/mod.rs @@ -26,7 +26,7 @@ use crate::buffer_len; pub type Display5in83 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 2) }, TriColor, >; diff --git a/src/epd7in5/mod.rs b/src/epd7in5/mod.rs index 121b780d..d7dbe63c 100644 --- a/src/epd7in5/mod.rs +++ b/src/epd7in5/mod.rs @@ -25,7 +25,7 @@ use crate::buffer_len; pub type Display7in5 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd7in5_hd/mod.rs b/src/epd7in5_hd/mod.rs index f78df1f2..614bbc41 100644 --- a/src/epd7in5_hd/mod.rs +++ b/src/epd7in5_hd/mod.rs @@ -28,7 +28,7 @@ use crate::buffer_len; pub type Display7in5 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd7in5_v2/mod.rs b/src/epd7in5_v2/mod.rs index 8d0d45f3..96c12b5d 100644 --- a/src/epd7in5_v2/mod.rs +++ b/src/epd7in5_v2/mod.rs @@ -29,7 +29,7 @@ use crate::buffer_len; pub type Display7in5 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd7in5b_v2/mod.rs b/src/epd7in5b_v2/mod.rs index 69963c57..565167db 100644 --- a/src/epd7in5b_v2/mod.rs +++ b/src/epd7in5b_v2/mod.rs @@ -31,7 +31,7 @@ use crate::buffer_len; pub type Display7in5 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 2) }, TriColor, >; diff --git a/src/graphics.rs b/src/graphics.rs index d24b535c..1275a95c 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use embedded_graphics_core::prelude::*; /// Display rotation, only 90° increments supported -#[derive(Clone, Copy, Default)] +#[derive(Default, Clone, Copy)] pub enum DisplayRotation { /// No rotation #[default] @@ -18,6 +18,32 @@ pub enum DisplayRotation { Rotate270, } +/// DisplayMode carries modes avaiable for color representaion on TriColor displays +/// Each mode contains list of (bw, color) pairs, where bw - value for BW layer, color - value for Chromatic layer +#[repr(u8)] +pub enum DisplayMode { + /// BwrBitOn chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic + /// Colors: White - (0xFF, 0); Black - (0, 0); Red - (0, 0xFF) + BwrBitOn = 0, + /// BwrBitOff is chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black + /// Colors: White - (0xFF, 0); Black - (0, 0); Red - (0xFF, 0xFF) + BwrBitOff = 1, + /// BwrBitOnColorInverted is same as standard, but with color layer values inverted + /// Colors: White - (0xFF, 0xFF); Black - (0, 0xFF); Red - (0, 0) + BwrBitOnColorInverted = 2, +} + +impl DisplayMode { + fn from_u8(value: u8) -> DisplayMode { + match value { + 0 => DisplayMode::BwrBitOn, + 1 => DisplayMode::BwrBitOff, + 2 => DisplayMode::BwrBitOnColorInverted, + v => panic!("Unsupported DisplayMode value {v}"), + } + } +} + /// count the number of bytes per line knowing that it may contains padding bits const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize { // round to upper 8 bit count @@ -29,13 +55,13 @@ const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize { /// /// - WIDTH: width in pixel when display is not rotated /// - HEIGHT: height in pixel when display is not rotated -/// - BWRBIT: mandatory value of the B/W when chromatic bit is set, can be any value for non +/// - MODE: mandatory value of the B/W when chromatic bit is set, can be any value for non /// tricolor epd /// - COLOR: color type used by the target display /// - BYTECOUNT: This is redundant with previous data and should be removed when const generic /// expressions are stabilized /// -/// More on BWRBIT: +/// More on MODE: /// /// Different chromatic displays differently treat the bits in chromatic color planes. /// Some of them ([crate::epd2in13bc]) will render a color pixel if bit is set for that pixel, @@ -44,12 +70,12 @@ const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize { /// Other displays, like [crate::epd5in83b_v2] in opposite, will draw color pixel if bit is /// cleared for that pixel, which is a `BWRBIT = false` mode. /// -/// BWRBIT=true: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic -/// BWRBIT=false: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black +/// MODE=true: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic +/// MODE=false: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black pub struct Display< const WIDTH: u32, const HEIGHT: u32, - const BWRBIT: bool, + const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, > { @@ -61,7 +87,7 @@ pub struct Display< impl< const WIDTH: u32, const HEIGHT: u32, - const BWRBIT: bool, + const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, > Default for Display @@ -89,7 +115,7 @@ impl< impl< const WIDTH: u32, const HEIGHT: u32, - const BWRBIT: bool, + const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, > DrawTarget for Display @@ -112,7 +138,7 @@ impl< impl< const WIDTH: u32, const HEIGHT: u32, - const BWRBIT: bool, + const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, > OriginDimensions for Display @@ -128,7 +154,7 @@ impl< impl< const WIDTH: u32, const HEIGHT: u32, - const BWRBIT: bool, + const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, > Display @@ -153,20 +179,13 @@ impl< /// Set a specific pixel color on this display pub fn set_pixel(&mut self, pixel: Pixel) { - set_pixel( - &mut self.buffer, - WIDTH, - HEIGHT, - self.rotation, - BWRBIT, - pixel, - ); + set_pixel(&mut self.buffer, WIDTH, HEIGHT, self.rotation, MODE, pixel); } } /// Some Tricolor specifics -impl - Display +impl + Display { /// get black/white internal buffer to use it (to draw in epd) pub fn bw_buffer(&self) -> &[u8] { @@ -185,7 +204,7 @@ impl { width: u32, height: u32, - bwrbit: bool, + mode: u8, buffer: &'a mut [u8], rotation: DisplayRotation, _color: PhantomData, @@ -232,17 +251,17 @@ impl<'a, COLOR: ColorType + PixelColor> VarDisplay<'a, COLOR> { /// You must allocate the buffer by yourself, it must be large enough to contain all pixels. /// /// Parameters are documented in `Display` as they are the same as the const generics there. - /// bwrbit should be false for non tricolor displays + /// MODE should be false for non tricolor displays pub fn new( width: u32, height: u32, buffer: &'a mut [u8], - bwrbit: bool, + mode: u8, ) -> Result { let myself = Self { width, height, - bwrbit, + mode, buffer, rotation: DisplayRotation::default(), _color: PhantomData, @@ -289,7 +308,7 @@ impl<'a, COLOR: ColorType + PixelColor> VarDisplay<'a, COLOR> { self.width, self.height, self.rotation, - self.bwrbit, + self.mode, pixel, ); } @@ -317,7 +336,7 @@ fn set_pixel( width: u32, height: u32, rotation: DisplayRotation, - bwrbit: bool, + mode: u8, pixel: Pixel, ) { let Pixel(point, color) = pixel; @@ -337,9 +356,10 @@ fn set_pixel( return; } + let display_mode = DisplayMode::from_u8(mode); let index = x as usize * COLOR::BITS_PER_PIXEL_PER_BUFFER / 8 + y as usize * line_bytes(width, COLOR::BITS_PER_PIXEL_PER_BUFFER); - let (mask, bits) = color.bitmask(bwrbit, x as u32); + let (mask, bits) = color.bitmask(display_mode, x as u32); if COLOR::BUFFER_COUNT == 2 { // split buffer is for tricolor displays that use 2 buffer for 2 bits per pixel @@ -364,14 +384,15 @@ mod tests { #[test] fn graphics_size() { // example definition taken from epd1in54 - let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let display = Display::<200, 200, { DisplayMode::BwrBitOff as u8 } + , { 200 * 200 / 8 }, Color>::default(); assert_eq!(display.buffer().len(), 5000); } // test default background color on all bytes #[test] fn graphics_default() { - let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let display = Display::<200, 200, { DisplayMode::BwrBitOff as u8 }, { 200 * 200 / 8 }, Color>::default(); for &byte in display.buffer() { assert_eq!(byte, 0); } @@ -379,7 +400,13 @@ mod tests { #[test] fn graphics_rotation_0() { - let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let mut display = Display::< + 200, + 200, + { DisplayMode::BwrBitOff as u8 }, + { 200 * 200 / 8 }, + Color, + >::default(); let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) .draw(&mut display); @@ -395,7 +422,13 @@ mod tests { #[test] fn graphics_rotation_90() { - let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let mut display = Display::< + 200, + 200, + { DisplayMode::BwrBitOff as u8 }, + { 200 * 200 / 8 }, + Color, + >::default(); display.set_rotation(DisplayRotation::Rotate90); let _ = Line::new(Point::new(0, 192), Point::new(0, 199)) .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) @@ -412,7 +445,13 @@ mod tests { #[test] fn graphics_rotation_180() { - let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let mut display = Display::< + 200, + 200, + { DisplayMode::BwrBitOff as u8 }, + { 200 * 200 / 8 }, + Color, + >::default(); display.set_rotation(DisplayRotation::Rotate180); let _ = Line::new(Point::new(192, 199), Point::new(199, 199)) .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) @@ -421,7 +460,7 @@ mod tests { let buffer = display.buffer(); extern crate std; - std::println!("{:?}", buffer); + std::println!("{buffer:?}"); assert_eq!(buffer[0], Color::Black.get_byte_value()); @@ -432,7 +471,13 @@ mod tests { #[test] fn graphics_rotation_270() { - let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let mut display = Display::< + 200, + 200, + { DisplayMode::BwrBitOff as u8 }, + { 200 * 200 / 8 }, + Color, + >::default(); display.set_rotation(DisplayRotation::Rotate270); let _ = Line::new(Point::new(199, 0), Point::new(199, 7)) .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) @@ -441,7 +486,7 @@ mod tests { let buffer = display.buffer(); extern crate std; - std::println!("{:?}", buffer); + std::println!("{buffer:?}"); assert_eq!(buffer[0], Color::Black.get_byte_value()); diff --git a/src/lib.rs b/src/lib.rs index 3ef7291a..0462e1ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ pub mod epd2in9bc; pub mod epd2in9d; pub mod epd3in7; pub mod epd4in2; +pub mod epd4in2bc; pub mod epd5in65f; pub mod epd5in83_v2; pub mod epd5in83b_v2; @@ -117,7 +118,7 @@ pub mod prelude { pub use crate::SPI_MODE; #[cfg(feature = "graphics")] - pub use crate::graphics::{Display, DisplayRotation}; + pub use crate::graphics::{Display, DisplayMode, DisplayRotation}; } /// Computes the needed buffer length. Takes care of rounding up in case width diff --git a/src/traits.rs b/src/traits.rs index abd2b3f9..dc066475 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -290,7 +290,7 @@ where ///let (x, y, frame_width, frame_height) = (20, 40, 80,80); /// ///let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 80 / 8 * 80]; -///let mut display = VarDisplay::new(frame_width, frame_height, &mut buffer,false).unwrap(); +///let mut display = VarDisplay::new(frame_width, frame_height, &mut buffer, DisplayMode::BwrBitOff as u8).unwrap(); /// ///epd.update_partial_old_frame(&mut spi, &mut delay, display.buffer(), x, y, frame_width, frame_height) /// .ok(); From 52826c0f42be71308112f6799ba4f5036a20f228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Trop=C3=A9e?= Date: Sun, 30 Apr 2023 19:40:44 +0200 Subject: [PATCH 2/6] Add example for epd4in2bc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Tropée --- examples/epd4in2bc.rs | 170 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 examples/epd4in2bc.rs diff --git a/examples/epd4in2bc.rs b/examples/epd4in2bc.rs new file mode 100644 index 00000000..4ab0eaba --- /dev/null +++ b/examples/epd4in2bc.rs @@ -0,0 +1,170 @@ +#![deny(warnings)] + +use embedded_graphics::{ + mono_font::MonoTextStyleBuilder, + prelude::*, + primitives::{Circle, Line, PrimitiveStyle}, + text::{Baseline, Text, TextStyleBuilder}, +}; +use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayMs; +//use embedded_hal::prelude::*; +use epd_waveshare::{ + color::*, + epd4in2bc::{Display4in2bc, Epd4in2bc}, + graphics::DisplayRotation, + prelude::*, +}; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, + sysfs_gpio::Direction, + Delay, Pin, Spidev, +}; + +// activate spi, gpio in raspi-config +// needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems +// see https://github.com/rust-embedded/rust-sysfs-gpio/issues/5 and follow-up issues +/** BCM Physical + * EPD_CS_PIN = 26; = 37 (On raspberry Pi put it on CE0 pin 8 physical 24) + EPD_BUSY_PIN = 24; = 18 + EPD_DC_PIN = 25; = 22 + EPD_RST_PIN = 17; = 11 + */ +fn main() -> Result<(), std::io::Error> { + // Configure SPI + // Settings are taken from + let mut spi = Spidev::open("/dev/spidev0.0").expect("spidev directory"); + let options = SpidevOptions::new() + .bits_per_word(8) + .max_speed_hz(4_000_000) + .mode(spidev::SpiModeFlags::SPI_MODE_0) + .build(); + spi.configure(&options).expect("spi configuration"); + + // Configure Digital I/O Pin to be used as Chip Select for SPI + let cs = Pin::new(26); //BCM7 CE0 + cs.export().expect("cs export"); + while !cs.is_exported() {} + cs.set_direction(Direction::Out).expect("CS Direction"); + cs.set_value(1).expect("CS Value set to 1"); + + let busy = Pin::new(24); //pin 29 + busy.export().expect("busy export"); + while !busy.is_exported() {} + busy.set_direction(Direction::In).expect("busy Direction"); + //busy.set_value(1).expect("busy Value set to 1"); + + let dc = Pin::new(25); //pin 31 //bcm6 + dc.export().expect("dc export"); + while !dc.is_exported() {} + dc.set_direction(Direction::Out).expect("dc Direction"); + dc.set_value(1).expect("dc Value set to 1"); + + let rst = Pin::new(17); //pin 36 //bcm16 + rst.export().expect("rst export"); + while !rst.is_exported() {} + rst.set_direction(Direction::Out).expect("rst Direction"); + rst.set_value(1).expect("rst Value set to 1"); + + let mut delay = Delay {}; + + let mut epd4in2bc = + Epd4in2bc::new(&mut spi, cs, busy, dc, rst, &mut delay,None).expect("eink initalize error"); + + println!("Test all the rotations"); + let mut display = Display4in2bc::default(); + display.clear(TriColor::White).ok(); + + display.set_rotation(DisplayRotation::Rotate0); + draw_text(&mut display, "Rotation 0!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate90); + draw_text(&mut display, "Rotation 90!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate180); + draw_text(&mut display, "Rotation 180!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate270); + draw_text(&mut display, "Rotation 270!", 5, 50); + + epd4in2bc.update_frame(&mut spi, display.buffer(), &mut delay)?; + epd4in2bc + .display_frame(&mut spi, &mut delay) + .expect("display frame new graphics"); + + delay.delay_ms(5000u16); + + println!("Now test new graphics with default rotation and three colors:"); + display.clear(TriColor::White).ok(); + + // draw a analog clock + let _ = Circle::with_center(Point::new(64, 64), 80) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(30, 40)) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 4)) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(80, 40)) + .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1)) + .draw(&mut display); + + // draw text white on Red background by using the chromatic buffer + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_6X10) + .text_color(TriColor::White) + .background_color(TriColor::Chromatic) + .build(); + let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); + + let _ = Text::with_text_style("It's working-WoB!", Point::new(90, 10), style, text_style) + .draw(&mut display); + + // use bigger/different font + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_10X20) + .text_color(TriColor::White) + .background_color(TriColor::Chromatic) + .build(); + + let _ = Text::with_text_style("It's working\nWoB!", Point::new(90, 40), style, text_style) + .draw(&mut display); + + // we used three colors, so we need to update both bw-buffer and chromatic-buffer + + epd4in2bc.update_color_frame( + &mut spi, + &mut delay, + display.bw_buffer(), + display.chromatic_buffer(), + )?; + epd4in2bc + .display_frame(&mut spi, &mut delay) + .expect("display frame new graphics"); + + println!("Second frame done. Waiting 5s"); + delay.delay_ms(5000u16); + + // clear both bw buffer and chromatic buffer + display.clear(TriColor::White).ok(); + epd4in2bc.update_color_frame( + &mut spi, + &mut delay, + display.bw_buffer(), + display.chromatic_buffer(), + )?; + epd4in2bc.display_frame(&mut spi, &mut delay)?; + + println!("Finished tests - going to sleep"); + epd4in2bc.sleep(&mut spi, &mut delay) +} + +fn draw_text(display: &mut Display4in2bc, text: &str, x: i32, y: i32) { + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_6X10) + .text_color(TriColor::Black) + .background_color(TriColor::White) + .build(); + + let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); + + let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(display); +} From ae4c68ef0b9959a238311937558507d8565381aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Trop=C3=A9e?= Date: Sun, 30 Apr 2023 19:49:44 +0200 Subject: [PATCH 3/6] Refresh whole frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Tropée --- examples/epd4in2bc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/epd4in2bc.rs b/examples/epd4in2bc.rs index 4ab0eaba..46bf9acf 100644 --- a/examples/epd4in2bc.rs +++ b/examples/epd4in2bc.rs @@ -86,7 +86,12 @@ fn main() -> Result<(), std::io::Error> { display.set_rotation(DisplayRotation::Rotate270); draw_text(&mut display, "Rotation 270!", 5, 50); - epd4in2bc.update_frame(&mut spi, display.buffer(), &mut delay)?; + epd4in2bc.update_color_frame( + &mut spi, + &mut delay, + display.bw_buffer(), + display.chromatic_buffer(), + )?; epd4in2bc .display_frame(&mut spi, &mut delay) .expect("display frame new graphics"); From 141a08a854dd0b1d345f845d672521d22ddb8984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Vojt=C3=A1=C5=A1ek?= Date: Fri, 21 Feb 2025 11:18:14 +0100 Subject: [PATCH 4/6] fixes after rebase --- examples/epd4in2bc.rs | 12 ++++----- src/color.rs | 2 +- src/epd1in02/mod.rs | 2 +- src/epd2in13b_v4/mod.rs | 2 +- src/epd2in66b/mod.rs | 2 +- src/epd2in7/mod.rs | 2 +- src/epd2in7_v2/mod.rs | 2 +- src/epd2in9b_v4/mod.rs | 2 +- src/epd2in9d/mod.rs | 2 +- src/epd4in2bc/mod.rs | 56 ++++++++++++++++++----------------------- src/epd5in83_v2/mod.rs | 2 +- src/epd7in3f/mod.rs | 2 +- src/graphics.rs | 8 +++--- 13 files changed, 44 insertions(+), 52 deletions(-) diff --git a/examples/epd4in2bc.rs b/examples/epd4in2bc.rs index 46bf9acf..325e3c1c 100644 --- a/examples/epd4in2bc.rs +++ b/examples/epd4in2bc.rs @@ -24,11 +24,11 @@ use linux_embedded_hal::{ // needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems // see https://github.com/rust-embedded/rust-sysfs-gpio/issues/5 and follow-up issues /** BCM Physical - * EPD_CS_PIN = 26; = 37 (On raspberry Pi put it on CE0 pin 8 physical 24) - EPD_BUSY_PIN = 24; = 18 - EPD_DC_PIN = 25; = 22 - EPD_RST_PIN = 17; = 11 - */ +* EPD_CS_PIN = 26; = 37 (On raspberry Pi put it on CE0 pin 8 physical 24) + EPD_BUSY_PIN = 24; = 18 + EPD_DC_PIN = 25; = 22 + EPD_RST_PIN = 17; = 11 +*/ fn main() -> Result<(), std::io::Error> { // Configure SPI // Settings are taken from @@ -68,7 +68,7 @@ fn main() -> Result<(), std::io::Error> { let mut delay = Delay {}; let mut epd4in2bc = - Epd4in2bc::new(&mut spi, cs, busy, dc, rst, &mut delay,None).expect("eink initalize error"); + Epd4in2bc::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error"); println!("Test all the rotations"); let mut display = Display4in2bc::default(); diff --git a/src/color.rs b/src/color.rs index 6a4c0abf..0c86e13d 100644 --- a/src/color.rs +++ b/src/color.rs @@ -82,7 +82,7 @@ pub trait ColorType { /// Return the data used to set a pixel color /// /// * MODE is used to tell the value of the unused bit when a chromatic - /// color is set (TriColor only as for now) + /// color is set (TriColor only as for now) /// * pos is the pixel position in the line, used to know which pixels must be set /// /// Return values are : diff --git a/src/epd1in02/mod.rs b/src/epd1in02/mod.rs index 6eafea13..1fd9f289 100644 --- a/src/epd1in02/mod.rs +++ b/src/epd1in02/mod.rs @@ -30,7 +30,7 @@ use self::constants::{ pub type Display1in02 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in13b_v4/mod.rs b/src/epd2in13b_v4/mod.rs index 5bedea9a..0cef56cc 100644 --- a/src/epd2in13b_v4/mod.rs +++ b/src/epd2in13b_v4/mod.rs @@ -78,7 +78,7 @@ const SINGLE_BYTE_WRITE: bool = true; pub type Display2in13b = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) * 2 }, TriColor, >; diff --git a/src/epd2in66b/mod.rs b/src/epd2in66b/mod.rs index 092fce3f..843b0ac6 100644 --- a/src/epd2in66b/mod.rs +++ b/src/epd2in66b/mod.rs @@ -197,7 +197,7 @@ pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White; pub type Display2in66b = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) * 2 }, TriColor, >; diff --git a/src/epd2in7/mod.rs b/src/epd2in7/mod.rs index 77cccca0..f2b29a14 100644 --- a/src/epd2in7/mod.rs +++ b/src/epd2in7/mod.rs @@ -35,7 +35,7 @@ use crate::buffer_len; pub type Display2in7 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in7_v2/mod.rs b/src/epd2in7_v2/mod.rs index b1687326..497313d2 100644 --- a/src/epd2in7_v2/mod.rs +++ b/src/epd2in7_v2/mod.rs @@ -38,7 +38,7 @@ const SINGLE_BYTE_WRITE: bool = true; pub type Display2in7 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd2in9b_v4/mod.rs b/src/epd2in9b_v4/mod.rs index 12cfdd90..f67b80a8 100644 --- a/src/epd2in9b_v4/mod.rs +++ b/src/epd2in9b_v4/mod.rs @@ -35,7 +35,7 @@ const IS_BUSY_LOW: bool = false; pub type Display2in9b = crate::graphics::Display< WIDTH, HEIGHT, - true, + { crate::graphics::DisplayMode::BwrBitOn as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 2) }, TriColor, >; diff --git a/src/epd2in9d/mod.rs b/src/epd2in9d/mod.rs index 50619060..da7acf01 100644 --- a/src/epd2in9d/mod.rs +++ b/src/epd2in9d/mod.rs @@ -43,7 +43,7 @@ use crate::buffer_len; pub type Display2in9d = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd4in2bc/mod.rs b/src/epd4in2bc/mod.rs index 0c9fe126..8cc109c5 100644 --- a/src/epd4in2bc/mod.rs +++ b/src/epd4in2bc/mod.rs @@ -49,10 +49,7 @@ //! //! BE CAREFUL! The screen can get ghosting/burn-ins through the Partial Fast Update Drawing. -use embedded_hal::{ - blocking::{delay::*, spi::Write}, - digital::v2::*, -}; +use embedded_hal::{delay::*, digital::*, spi::SpiDevice}; use crate::interface::DisplayInterface; use crate::traits::{ @@ -70,6 +67,7 @@ use crate::epd4in2::WIDTH; /// Default Background Color pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White; const IS_BUSY_LOW: bool = true; +const SINGLE_BYTE_WRITE: bool = true; use crate::buffer_len; @@ -85,24 +83,23 @@ pub type Display4in2bc = crate::graphics::Display< /// Epd4in2bc driver /// -pub struct Epd4in2bc { +pub struct Epd4in2bc { /// Connection Interface - interface: DisplayInterface, + interface: DisplayInterface, /// Background Color color: TriColor, /// Refresh LUT refresh: RefreshLut, } -impl InternalWiAdditions - for Epd4in2bc +impl InternalWiAdditions + for Epd4in2bc where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayNs, { fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { // reset the device @@ -147,15 +144,14 @@ where } } -impl WaveshareThreeColorDisplay - for Epd4in2bc +impl WaveshareThreeColorDisplay + for Epd4in2bc where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayNs, { fn update_color_frame( &mut self, @@ -199,28 +195,26 @@ where } } -impl WaveshareDisplay - for Epd4in2bc +impl WaveshareDisplay + for Epd4in2bc where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayNs, { type DisplayColor = TriColor; fn new( spi: &mut SPI, - cs: CS, busy: BUSY, dc: DC, rst: RST, delay: &mut DELAY, delay_us: Option, ) -> Result { - let interface = DisplayInterface::new(cs, busy, dc, rst, delay_us); + let interface = DisplayInterface::new(busy, dc, rst, delay_us); let mut epd = Epd4in2bc { interface, @@ -407,14 +401,13 @@ where } } -impl Epd4in2bc +impl Epd4in2bc where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayNs, { fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> { self.interface.cmd(spi, command) @@ -502,15 +495,14 @@ where } } -impl QuickRefresh - for Epd4in2bc +impl QuickRefresh + for Epd4in2bc where - SPI: Write, - CS: OutputPin, + SPI: SpiDevice, BUSY: InputPin, DC: OutputPin, RST: OutputPin, - DELAY: DelayUs, + DELAY: DelayNs, { /// To be followed immediately after by `update_old_frame`. fn update_old_frame( diff --git a/src/epd5in83_v2/mod.rs b/src/epd5in83_v2/mod.rs index 58909b2c..48b9c4c3 100644 --- a/src/epd5in83_v2/mod.rs +++ b/src/epd5in83_v2/mod.rs @@ -26,7 +26,7 @@ use crate::buffer_len; pub type Display5in83 = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize) }, Color, >; diff --git a/src/epd7in3f/mod.rs b/src/epd7in3f/mod.rs index 11a733df..2d789b75 100644 --- a/src/epd7in3f/mod.rs +++ b/src/epd7in3f/mod.rs @@ -28,7 +28,7 @@ mod command; pub type Display7in3f = crate::graphics::Display< WIDTH, HEIGHT, - false, + { crate::graphics::DisplayMode::BwrBitOff as u8 }, { buffer_len(WIDTH as usize, HEIGHT as usize * 4) }, OctColor, >; diff --git a/src/graphics.rs b/src/graphics.rs index 1275a95c..b145d975 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -90,7 +90,7 @@ impl< const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, - > Default for Display + > Default for Display { /// Initialize display with the color '0', which may not be the same on all device. /// Many devices have a bit parameter polarity that should be changed if this is not the right @@ -118,7 +118,7 @@ impl< const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, - > DrawTarget for Display + > DrawTarget for Display { type Color = COLOR; type Error = core::convert::Infallible; @@ -141,7 +141,7 @@ impl< const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, - > OriginDimensions for Display + > OriginDimensions for Display { fn size(&self) -> Size { match self.rotation { @@ -157,7 +157,7 @@ impl< const MODE: u8, const BYTECOUNT: usize, COLOR: ColorType + PixelColor, - > Display + > Display { /// get internal buffer to use it (to draw in epd) pub fn buffer(&self) -> &[u8] { From 556b56032a7d5c6caf3fe516dc672e3e64a81377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Vojt=C3=A1=C5=A1ek?= Date: Fri, 21 Feb 2025 11:37:03 +0100 Subject: [PATCH 5/6] fix tests --- examples/epd4in2bc.rs | 21 ++++++++++----------- src/color.rs | 12 ++++++------ src/epd4in2bc/mod.rs | 16 ++++++++-------- src/graphics.rs | 12 ++++++++++-- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/examples/epd4in2bc.rs b/examples/epd4in2bc.rs index 325e3c1c..94fc4490 100644 --- a/examples/epd4in2bc.rs +++ b/examples/epd4in2bc.rs @@ -6,8 +6,7 @@ use embedded_graphics::{ primitives::{Circle, Line, PrimitiveStyle}, text::{Baseline, Text, TextStyleBuilder}, }; -use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayMs; -//use embedded_hal::prelude::*; +use embedded_hal::delay::DelayNs; use epd_waveshare::{ color::*, epd4in2bc::{Display4in2bc, Epd4in2bc}, @@ -17,7 +16,7 @@ use epd_waveshare::{ use linux_embedded_hal::{ spidev::{self, SpidevOptions}, sysfs_gpio::Direction, - Delay, Pin, Spidev, + Delay, SPIError, SpidevDevice, SysfsPin, }; // activate spi, gpio in raspi-config @@ -29,10 +28,10 @@ use linux_embedded_hal::{ EPD_DC_PIN = 25; = 22 EPD_RST_PIN = 17; = 11 */ -fn main() -> Result<(), std::io::Error> { +fn main() -> Result<(), SPIError> { // Configure SPI // Settings are taken from - let mut spi = Spidev::open("/dev/spidev0.0").expect("spidev directory"); + let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory"); let options = SpidevOptions::new() .bits_per_word(8) .max_speed_hz(4_000_000) @@ -41,25 +40,25 @@ fn main() -> Result<(), std::io::Error> { spi.configure(&options).expect("spi configuration"); // Configure Digital I/O Pin to be used as Chip Select for SPI - let cs = Pin::new(26); //BCM7 CE0 + let cs = SysfsPin::new(26); //BCM7 CE0 cs.export().expect("cs export"); while !cs.is_exported() {} cs.set_direction(Direction::Out).expect("CS Direction"); cs.set_value(1).expect("CS Value set to 1"); - let busy = Pin::new(24); //pin 29 + let busy = SysfsPin::new(24); //pin 29 busy.export().expect("busy export"); while !busy.is_exported() {} busy.set_direction(Direction::In).expect("busy Direction"); //busy.set_value(1).expect("busy Value set to 1"); - let dc = Pin::new(25); //pin 31 //bcm6 + let dc = SysfsPin::new(25); //pin 31 //bcm6 dc.export().expect("dc export"); while !dc.is_exported() {} dc.set_direction(Direction::Out).expect("dc Direction"); dc.set_value(1).expect("dc Value set to 1"); - let rst = Pin::new(17); //pin 36 //bcm16 + let rst = SysfsPin::new(17); //pin 36 //bcm16 rst.export().expect("rst export"); while !rst.is_exported() {} rst.set_direction(Direction::Out).expect("rst Direction"); @@ -96,7 +95,7 @@ fn main() -> Result<(), std::io::Error> { .display_frame(&mut spi, &mut delay) .expect("display frame new graphics"); - delay.delay_ms(5000u16); + delay.delay_ms(5000); println!("Now test new graphics with default rotation and three colors:"); display.clear(TriColor::White).ok(); @@ -146,7 +145,7 @@ fn main() -> Result<(), std::io::Error> { .expect("display frame new graphics"); println!("Second frame done. Waiting 5s"); - delay.delay_ms(5000u16); + delay.delay_ms(5000); // clear both bw buffer and chromatic buffer display.clear(TriColor::White).ok(); diff --git a/src/color.rs b/src/color.rs index 0c86e13d..99590c79 100644 --- a/src/color.rs +++ b/src/color.rs @@ -539,28 +539,28 @@ mod tests { #[test] fn test_tricolor_bitmask() { assert_eq!( - TriColor::Black.bitmask(false, 0), + TriColor::Black.bitmask(DisplayMode::BwrBitOff, 0), (0b01111111, u16::from_le_bytes([0b00000000, 0b00000000])) ); assert_eq!( - TriColor::White.bitmask(false, 0), + TriColor::White.bitmask(DisplayMode::BwrBitOff, 0), (0b01111111, u16::from_le_bytes([0b10000000, 0b00000000])) ); assert_eq!( - TriColor::Chromatic.bitmask(false, 0), + TriColor::Chromatic.bitmask(DisplayMode::BwrBitOff, 0), (0b01111111, u16::from_le_bytes([0b10000000, 0b10000000])) ); assert_eq!( - TriColor::Black.bitmask(true, 0), + TriColor::Black.bitmask(DisplayMode::BwrBitOn, 0), (0b01111111, u16::from_le_bytes([0b00000000, 0b00000000])) ); assert_eq!( - TriColor::White.bitmask(true, 0), + TriColor::White.bitmask(DisplayMode::BwrBitOn, 0), (0b01111111, u16::from_le_bytes([0b10000000, 0b00000000])) ); assert_eq!( - TriColor::Chromatic.bitmask(true, 0), + TriColor::Chromatic.bitmask(DisplayMode::BwrBitOn, 0), (0b01111111, u16::from_le_bytes([0b00000000, 0b10000000])) ); } diff --git a/src/epd4in2bc/mod.rs b/src/epd4in2bc/mod.rs index 8cc109c5..ad86e588 100644 --- a/src/epd4in2bc/mod.rs +++ b/src/epd4in2bc/mod.rs @@ -8,8 +8,8 @@ //! # Examples //! //!```rust, no_run -//!# use embedded_hal_mock::*; -//!# fn main() -> Result<(), MockError> { +//!# use embedded_hal_mock::eh1::*; +//!# fn main() -> Result<(), embedded_hal::spi::ErrorKind> { //!use embedded_graphics::{ //! prelude::*, primitives::{Line, PrimitiveStyle}, //!}; @@ -18,14 +18,14 @@ //!# let expectations = []; //!# let mut spi = spi::Mock::new(&expectations); //!# let expectations = []; -//!# let cs_pin = pin::Mock::new(&expectations); -//!# let busy_in = pin::Mock::new(&expectations); -//!# let dc = pin::Mock::new(&expectations); -//!# let rst = pin::Mock::new(&expectations); -//!# let mut delay = delay::MockNoop::new(); +//!# let cs_pin = digital::Mock::new(&expectations); +//!# let busy_in = digital::Mock::new(&expectations); +//!# let dc = digital::Mock::new(&expectations); +//!# let rst = digital::Mock::new(&expectations); +//!# let mut delay = delay::NoopDelay::new(); //! //!// Setup EPD -//!let mut epd = Epd4in2bc::new(&mut spi, cs_pin, busy_in, dc, rst, &mut delay, None)?; +//!let mut epd = Epd4in2bc::new(&mut spi, busy_in, dc, rst, &mut delay, None)?; //! //!// Use display graphics from embedded-graphics //!let mut display = Display4in2bc::default(); diff --git a/src/graphics.rs b/src/graphics.rs index b145d975..8b96ac76 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -497,7 +497,13 @@ mod tests { #[test] fn graphics_set_pixel_tricolor_false() { - let mut display = Display::<4, 4, false, { 4 * 4 * 2 / 8 }, TriColor>::default(); + let mut display = Display::< + 4, + 4, + { DisplayMode::BwrBitOff as u8 }, + { 4 * 4 * 2 / 8 }, + TriColor, + >::default(); display.set_pixel(Pixel(Point::new(0, 0), TriColor::White)); display.set_pixel(Pixel(Point::new(1, 0), TriColor::Chromatic)); display.set_pixel(Pixel(Point::new(2, 0), TriColor::Black)); @@ -515,7 +521,9 @@ mod tests { #[test] fn graphics_set_pixel_tricolor_true() { - let mut display = Display::<4, 4, true, { 4 * 4 * 2 / 8 }, TriColor>::default(); + let mut display = + Display::<4, 4, { DisplayMode::BwrBitOn as u8 }, { 4 * 4 * 2 / 8 }, TriColor>::default( + ); display.set_pixel(Pixel(Point::new(0, 0), TriColor::White)); display.set_pixel(Pixel(Point::new(1, 0), TriColor::Chromatic)); display.set_pixel(Pixel(Point::new(2, 0), TriColor::Black)); From aecabf21c2563493c4d709ca39e6368baea20671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Vojt=C3=A1=C5=A1ek?= Date: Fri, 21 Feb 2025 14:34:33 +0100 Subject: [PATCH 6/6] update docs --- src/graphics.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/graphics.rs b/src/graphics.rs index 8b96ac76..2e8cdbc2 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -65,13 +65,17 @@ const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize { /// /// Different chromatic displays differently treat the bits in chromatic color planes. /// Some of them ([crate::epd2in13bc]) will render a color pixel if bit is set for that pixel, -/// which is a `BWRBIT = true` mode. +/// which is a `MODE = BwrBitOn` mode. /// /// Other displays, like [crate::epd5in83b_v2] in opposite, will draw color pixel if bit is -/// cleared for that pixel, which is a `BWRBIT = false` mode. +/// cleared for that pixel, which is a `MODE = BwrBitOff` mode. /// -/// MODE=true: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic -/// MODE=false: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black +/// A few displays, like [crate::epd4in2bc] will draw color pixel if bit is set for that pixel, +/// but will invert the color values, which is a `MODE = BwrBitOnColorInverted` mode. +/// +/// MODE=BwrBitOn: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic +/// MODE=BwrBitOff: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black +/// MODE=BwrBitOnColorInverted: same as standard, but with color layer values inverted pub struct Display< const WIDTH: u32, const HEIGHT: u32,