Skip to content

Commit

Permalink
experiment with other display modes
Browse files Browse the repository at this point in the history
  • Loading branch information
pbert519 committed Jun 5, 2024
1 parent a96e083 commit 6f8933a
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 7 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ This driver can be used with the embedded graphics trait.
## Details
- IT8951 has a image load engine which can convert pixel data before storing it in the local frame buffer
- It is possible to read and write the memory directly without using the image load engine
- **Important** Data must be always aligned to 16bit words!
- **Important** Data must be always aligned to 16bit words!


# TODO
- Use 1bbp or 2 bpp if possible
- Remove busy wait states
- Staging buffer analysis
- implement fill solid
106 changes: 106 additions & 0 deletions examples/esp32/src/demo.rs
Original file line number Diff line number Diff line change
@@ -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<impl IT8951Interface, Run>,
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<impl IT8951Interface, Run>) {
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<impl IT8951Interface, Run>) {
epd.clear(Gray4::WHITE).unwrap();
}

pub fn run(epd: &mut it8951::IT8951<impl IT8951Interface, Run>) {
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);
}
16 changes: 13 additions & 3 deletions examples/esp32/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
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};

mod demo;

fn main() -> ! {
// Bind the log crate to the ESP Logging facilities
Expand Down Expand Up @@ -64,10 +70,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> {
Expand Down
45 changes: 44 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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
Expand Down Expand Up @@ -382,6 +394,35 @@ impl<IT8951Interface: interface::IT8951Interface> IT8951<IT8951Interface, Run> {
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> {
Expand Down Expand Up @@ -497,7 +538,9 @@ impl<IT8951Interface: interface::IT8951Interface> DrawTarget for IT8951<IT8951In

fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
let raw_color = color.luma() as u16;
self.clear_frame_buffer(raw_color)
self.display_area_with_color(&AreaImgInfo::with_size(self.get_dev_info().panel_width, self.get_dev_info().panel_width), WaveformMode::GrayscaleClearing16, raw_color)?;
//self.clear_frame_buffer(raw_color)
Ok(())
}

fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
Expand Down
23 changes: 23 additions & 0 deletions src/memory_converter_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -47,6 +60,16 @@ pub struct MemoryConverterSetting {
pub rotation: MemoryConverterRotation,
}

impl Default for MemoryConverterSetting {
fn default() -> Self {
Self {
endianness: MemoryConverterEndianness::LittleEndian,
bit_per_pixel: MemoryConverterBitPerPixel::BitsPerPixel4,
rotation: MemoryConverterRotation::Rotate0,
}
}
}

impl MemoryConverterSetting {
pub(crate) fn into_arg(self) -> u16 {
let endianness = self.endianness as u16;
Expand Down
4 changes: 2 additions & 2 deletions src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 6f8933a

Please sign in to comment.