Added QuickRefresh trait, and implemented it for 4.2 display
parent
87e63741ef
commit
c30d213bc5
|
|
@ -1,5 +1,11 @@
|
|||
//! This file contains look-up-tables used to set voltages used during
|
||||
//! various categories of pixel refreshes.
|
||||
|
||||
#[rustfmt::skip]
|
||||
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, 0x17, 0x00, 0x00, 0x02,
|
||||
0x00, 0x0A, 0x01, 0x00, 0x00, 0x01,
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ use embedded_hal::{
|
|||
};
|
||||
|
||||
use crate::interface::DisplayInterface;
|
||||
use crate::traits::{InternalWiAdditions, RefreshLUT, WaveshareDisplay};
|
||||
use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLUT, WaveshareDisplay};
|
||||
|
||||
//The Lookup Tables for the Display
|
||||
mod constants;
|
||||
|
|
@ -407,6 +407,174 @@ where
|
|||
self.cmd_with_data(spi, Command::LUT_BLACK_TO_BLACK, 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<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)]
|
||||
|
|
|
|||
|
|
@ -87,7 +87,9 @@ pub(crate) mod type_a;
|
|||
/// Includes everything important besides the chosen Display
|
||||
pub mod prelude {
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -229,3 +229,67 @@ where
|
|||
/// if the device is still busy
|
||||
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…
Reference in New Issue