Browse Source

Added QuickRefresh trait, and implemented it for 4.2 display

main
David-OConnor 5 years ago
parent
commit
c30d213bc5
  1. 6
      src/epd4in2/constants.rs
  2. 170
      src/epd4in2/mod.rs
  3. 4
      src/lib.rs
  4. 64
      src/traits.rs

6
src/epd4in2/constants.rs

@ -1,5 +1,11 @@
//! This file contains look-up-tables used to set voltages used during
//! various categories of pixel refreshes.
#[rustfmt::skip] #[rustfmt::skip]
pub(crate) const LUT_VCOM0: [u8; 44] = [ pub(crate) const LUT_VCOM0: [u8; 44] = [
// The commented-out line below was used in a Ben Krasnow video explaining
// partial refreshes.
// 0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02,
0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02,
0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01,

170
src/epd4in2/mod.rs

@ -55,7 +55,7 @@ use embedded_hal::{
}; };
use crate::interface::DisplayInterface; use crate::interface::DisplayInterface;
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay}; use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLUT, WaveshareDisplay};
//The Lookup Tables for the Display //The Lookup Tables for the Display
mod constants; mod constants;
@ -407,6 +407,174 @@ where
self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?; self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, lut_bb)?;
Ok(()) 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<SPI, CS, BUSY, DC, RST> QuickRefresh<SPI, CS, BUSY, DC, RST>
for EPD4in2<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
{
/// To be followed immediately after by `update_old_frame`.
fn update_old_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
self.wait_until_idle();
// todo: Eval if you need these 3 res setting items.
self.send_resolution(spi)?;
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
//VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x97])?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
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]) -> Result<(), SPI::Error> {
self.wait_until_idle();
// self.send_resolution(spi)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.data(spi, buffer)?;
Ok(())
}
fn update_partial_old_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle();
// todo: Eval if you need these 3 res setting items.
self.send_resolution(spi)?;
self.interface
.cmd_with_data(spi, Command::VCM_DC_SETTING, &[0x12])?;
//VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
self.interface
.cmd_with_data(spi, Command::VCOM_AND_DATA_INTERVAL_SETTING, &[0x97])?;
if buffer.len() as u32 != width / 8 * height {
//TODO: panic!! or sth like that
//return Err("Wrong buffersize");
}
self.interface.cmd(spi, Command::PARTIAL_IN)?;
self.interface.cmd(spi, Command::PARTIAL_WINDOW)?;
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
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,
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 sth like that
//return Err("Wrong buffersize");
}
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface.data(spi, buffer)?;
self.interface.cmd(spi, Command::PARTIAL_OUT)?;
Ok(())
}
fn clear_partial_frame(
&mut self,
spi: &mut SPI,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error> {
self.wait_until_idle();
self.send_resolution(spi)?;
let color_value = self.color.get_byte_value();
self.interface.cmd(spi, Command::PARTIAL_IN)?;
self.interface.cmd(spi, Command::PARTIAL_WINDOW)?;
self.shift_display(spi, x, y, width, height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_1)?;
self.interface
.data_x_times(spi, color_value, width / 8 * height)?;
self.interface
.cmd(spi, Command::DATA_START_TRANSMISSION_2)?;
self.interface
.data_x_times(spi, color_value, width / 8 * height)?;
self.interface.cmd(spi, Command::PARTIAL_OUT)?;
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

4
src/lib.rs

@ -87,7 +87,9 @@ pub(crate) mod type_a;
/// Includes everything important besides the chosen Display /// Includes everything important besides the chosen Display
pub mod prelude { pub mod prelude {
pub use crate::color::{Color, OctColor, TriColor}; pub use crate::color::{Color, OctColor, TriColor};
pub use crate::traits::{RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay}; pub use crate::traits::{
QuickRefresh, RefreshLUT, WaveshareDisplay, WaveshareThreeColorDisplay,
};
pub use crate::SPI_MODE; pub use crate::SPI_MODE;

64
src/traits.rs

@ -229,3 +229,67 @@ where
/// if the device is still busy /// if the device is still busy
fn is_busy(&self) -> bool; fn is_busy(&self) -> bool;
} }
/// Allows quick refresh support for displays that support it; lets you send both
/// old and new frame data to support this.
///
/// When using the quick refresh look-up table, the display must receive separate display
/// buffer data marked as old, and new. This is used to determine which pixels need to change,
/// and how they will change. This isn't required when using full refreshes.
///
/// Example:
/// epd.update_partial_old_frame(spi, disp.buffer(), x, y, frame_width, frame_height)
/// .ok();
///
/// disp.clear_buffer(Color::White);
/// // Execute drawing commands here.
///
/// epd.update_partial_new_frame(spi, disp.buffer(), x, y, frame_width, frame_height)
/// .ok();
pub trait QuickRefresh<SPI, CS, BUSY, DC, RST>
where
SPI: Write<u8>,
CS: OutputPin,
BUSY: InputPin,
DC: OutputPin,
RST: OutputPin,
{
/// Updates the old frame.
fn update_old_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error>;
/// Updates the new frame.
fn update_new_frame(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error>;
/// Updates the old frame for a portion of the display.
fn update_partial_old_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error>;
/// Updates the new frame for a portion of the display.
fn update_partial_new_frame(
&mut self,
spi: &mut SPI,
buffer: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error>;
/// Clears the partial frame buffer on the EPD with the declared background color
/// The background color can be changed with [`WaveshareDisplay::set_background_color`]
fn clear_partial_frame(
&mut self,
spi: &mut SPI,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), SPI::Error>;
}

Loading…
Cancel
Save