From f3e26146c464753b12ed9461f1e95b538fe7001b Mon Sep 17 00:00:00 2001 From: pbert Date: Wed, 5 Jun 2024 13:11:05 +0200 Subject: [PATCH] experiment with other display modes --- README.md | 2 + examples/esp32/src/demo.rs | 106 +++++++++++++++++++++++++++++++ examples/esp32/src/main.rs | 15 ++++- src/lib.rs | 62 ++++++++++++++---- src/memory_converter_settings.rs | 13 ++++ src/register.rs | 4 +- 6 files changed, 187 insertions(+), 15 deletions(-) create mode 100644 examples/esp32/src/demo.rs diff --git a/README.md b/README.md index 9ef684d..89b2025 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ This driver can be used with the embedded graphics trait, currently only suppori - Support display engine fill area - Support display engine 1 bit per pixel mode - Support static buffer allocations +- Use 1bbp or 2 bpp if possible + ## Changelog diff --git a/examples/esp32/src/demo.rs b/examples/esp32/src/demo.rs new file mode 100644 index 0000000..6e8e88d --- /dev/null +++ b/examples/esp32/src/demo.rs @@ -0,0 +1,106 @@ +// based on https://github.com/waveshareteam/IT8951-ePaper/blob/master/Raspberry +use embedded_graphics::{ + draw_target::DrawTarget, + pixelcolor::{Gray4, GrayColor}, +}; +use it8951::{ + interface::*, + memory_converter_settings::{MemoryConverterBitPerPixel, MemoryConverterSetting}, + *, +}; +use std::time::Instant; + +pub fn display_color_palette_example( + epd: &mut it8951::IT8951, + bpp: MemoryConverterBitPerPixel, +) { + // create stripes for each color and display it + let width = epd.get_dev_info().panel_width as usize; + let height = 85; + let pixels = width * height / bpp.pixel_per_byte() / 2; + let mut buf = vec![0u16; pixels]; + + let pattern = [ + 0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xAAAA, + 0xBBBB, 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF, + ]; + + let before = Instant::now(); + + for i in 0..pattern.len() { + buf.fill(pattern[i]); + // write buff & display + + epd.load_image_area( + epd.get_dev_info().memory_address, + MemoryConverterSetting { + bit_per_pixel: bpp, + ..MemoryConverterSetting::default() + }, + &AreaImgInfo { + area_x: 0, + area_y: (i * height) as u16, + area_w: width as u16, + area_h: height as u16, + }, + &buf, + ) + .unwrap(); + println!( + "all Elapsed time: {:.2?}, written chunk {}", + before.elapsed(), + i + ); + } + + epd.display(it8951::WaveformMode::GrayscaleClearing16) + .unwrap(); + + println!("Elapsed time: {:.2?}", before.elapsed()); +} + +pub fn display_area_1bpp(epd: &mut it8951::IT8951) { + let pattern = [ + 0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xAAAA, + 0xBBBB, 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF, + ]; + let height = 85; + + let before = Instant::now(); + + for i in 0..pattern.len() { + let area = AreaImgInfo { + area_x: 0, + area_y: (i * height) as u16, + area_w: 1872 as u16, + area_h: height as u16, + }; + + epd.display_area_with_color(&area, WaveformMode::GrayscaleClearing16, pattern[i]) + .unwrap(); + println!("Elapsed time: {:.2?}", before.elapsed()); + } + println!("Elapsed time: {:.2?}", before.elapsed()); + +} + +pub fn clear_refresh(epd: &mut it8951::IT8951) { + epd.clear(Gray4::WHITE).unwrap(); +} + +pub fn run(epd: &mut it8951::IT8951) { + clear_refresh(epd); + + // 16 color grayscale + display_color_palette_example(epd, MemoryConverterBitPerPixel::BitsPerPixel4); + std::thread::sleep(std::time::Duration::from_secs(10)); + clear_refresh(epd); + // 4 color grayscale + display_color_palette_example(epd, MemoryConverterBitPerPixel::BitsPerPixel2); + std::thread::sleep(std::time::Duration::from_secs(10)); + clear_refresh(epd); + // 2 color grayscale + display_area_1bpp(epd); + std::thread::sleep(std::time::Duration::from_secs(10)); + clear_refresh(epd); +} diff --git a/examples/esp32/src/main.rs b/examples/esp32/src/main.rs index c299ff2..5ae68c3 100644 --- a/examples/esp32/src/main.rs +++ b/examples/esp32/src/main.rs @@ -1,9 +1,16 @@ +use embedded_graphics::{ + pixelcolor::Gray4, + prelude::*, + primitives::{PrimitiveStyle, Rectangle}, +}; use esp_idf_hal::{delay::Ets, gpio::PinDriver, prelude::*, spi::*}; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use it8951::{interface::*, *}; use embedded_graphics::{prelude::*, primitives::{Rectangle, PrimitiveStyle}, pixelcolor::Gray4}; use it8951::Config; +mod demo; + fn main() -> ! { // Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default(); @@ -65,10 +72,14 @@ fn main() -> ! { epd.display(it8951::WaveformMode::GL16).unwrap(); + demo::run(&mut epd); - let epd = epd.standby().unwrap(); + let _epd = epd.standby().unwrap(); - loop {} + loop { + println!("Reached main loop, sleep!"); + std::thread::sleep(std::time::Duration::from_secs(1)); + } } fn setup_watchdog() -> Result<(), esp_idf_sys::EspError> { diff --git a/src/lib.rs b/src/lib.rs index 5f29282..1b10473 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,18 @@ pub struct AreaImgInfo { pub area_h: u16, } +impl AreaImgInfo { + /// Creates a area image info with the given size and the origin (0, 0) + pub fn with_size(width: u16, height: u16) -> Self { + Self { + area_x: 0, + area_y: 0, + area_w: width, + area_h: height, + } + } +} + /// See https://www.waveshare.com/w/upload/c/c4/E-paper-mode-declaration.pdf for full description pub enum WaveformMode { /// used for full erase to white, flashy, should be used if framebuffer is not up to date @@ -388,6 +400,35 @@ impl IT8951 { Ok(()) } + /// Display the defined area, but interpret the framebuffer as 1 bit per pixel. + /// the grayscale value for foreground and background are given by the parameters as fg_color and bg_color. + pub fn display_area_with_color( + &mut self, + area_info: &AreaImgInfo, + mode: WaveformMode, + color: u16, + ) -> Result<(), Error> { + self.wait_for_display_ready()?; + + // enable 1bpp mode + let reg_value = self.read_register(register::UP1SR + 2)?; + self.write_register(register::UP1SR + 2, reg_value | (1u16 << 2))?; + + // write color values + //let color_value = (fg_color as u16) << 8 | (bg_color as u16); + self.write_register(register::BGVR, color)?; + + // update display + self.display_area(area_info, mode)?; + self.wait_for_display_ready()?; + + // disable 1bpp mode + let reg_value = self.read_register(register::UP1SR + 2)?; + self.write_register(register::UP1SR + 2, reg_value & !(1u16 << 2))?; + + Ok(()) + } + // misc ------------------------------------------------------------------------------------------------ fn wait_for_display_ready(&mut self) -> Result<(), Error> { @@ -513,18 +554,17 @@ impl DrawTarget for IT8951 Result<(), Self::Error> { - let info = self.get_dev_info(); - - self.fill_solid( - &Rectangle::new( - Point::zero(), - Size { - width: info.panel_width as u32, - height: info.panel_height as u32, - }, + let raw_color = color.luma() as u16; + self.display_area_with_color( + &AreaImgInfo::with_size( + self.get_dev_info().panel_width, + self.get_dev_info().panel_width, ), - color, - ) + WaveformMode::GrayscaleClearing16, + raw_color, + )?; + //self.clear_frame_buffer(raw_color) + Ok(()) } fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> { diff --git a/src/memory_converter_settings.rs b/src/memory_converter_settings.rs index 6671a67..c5d69d6 100644 --- a/src/memory_converter_settings.rs +++ b/src/memory_converter_settings.rs @@ -11,6 +11,7 @@ pub enum MemoryConverterEndianness { /// Bits per pixel /// the pixel data send to the controller can encode the pixels with a different number of bits +#[derive(Clone, Copy, Debug)] #[repr(u16)] pub enum MemoryConverterBitPerPixel { /// each pixel value is given by 2 bits @@ -23,6 +24,18 @@ pub enum MemoryConverterBitPerPixel { BitsPerPixel8 = 0b11, } +impl MemoryConverterBitPerPixel { + /// Returns the number of pixels per byte + pub fn pixel_per_byte(&self) -> usize { + match self { + MemoryConverterBitPerPixel::BitsPerPixel2 => 4, + MemoryConverterBitPerPixel::BitsPerPixel3 => unimplemented!(), + MemoryConverterBitPerPixel::BitsPerPixel4 => 2, + MemoryConverterBitPerPixel::BitsPerPixel8 => 1, + } + } +} + /// The memory converter supports rotating the written pixel data #[repr(u16)] pub enum MemoryConverterRotation { diff --git a/src/register.rs b/src/register.rs index e370954..331ad1d 100644 --- a/src/register.rs +++ b/src/register.rs @@ -13,12 +13,12 @@ const _LUT01AF: u16 = DISPLAY_REG_BASE + 0x114; //LUT0 and LUT1 Active Flag Reg //Update Parameter Setting Register const _UP0SR: u16 = DISPLAY_REG_BASE + 0x134; //Update Parameter0 Setting Reg -const _UP1SR: u16 = DISPLAY_REG_BASE + 0x138; //Update Parameter1 Setting Reg +pub const UP1SR: u16 = DISPLAY_REG_BASE + 0x138; //Update Parameter1 Setting Reg const _LUT0ABFRV: u16 = DISPLAY_REG_BASE + 0x13C; //LUT0 Alpha blend and Fill rectangle Value const _UPBBADDR: u16 = DISPLAY_REG_BASE + 0x17C; //Update Buffer Base Address const _LUT0IMXY: u16 = DISPLAY_REG_BASE + 0x180; //LUT0 Image buffer X/Y offset Reg pub const LUTAFSR: u16 = DISPLAY_REG_BASE + 0x224; //LUT Status Reg (status of All LUT Engines) -const _BGVR: u16 = DISPLAY_REG_BASE + 0x250; //Bitmap (1bpp) image color table +pub const BGVR: u16 = DISPLAY_REG_BASE + 0x250; //Bitmap (1bpp) image color table //System Registers const SYS_REG_BASE: u16 = 0x0000;