diff --git a/src/epd2in13b_v4/mod.rs b/src/epd2in13b_v4/mod.rs index 5bedea9a..51801ca0 100644 --- a/src/epd2in13b_v4/mod.rs +++ b/src/epd2in13b_v4/mod.rs @@ -50,6 +50,9 @@ //!# Ok(()) //!# } //!``` +use core::convert::Infallible; + +use embedded_graphics_core::prelude::DrawTarget; // Original Waveforms from Waveshare use embedded_hal::{ delay::DelayNs, @@ -57,12 +60,12 @@ use embedded_hal::{ spi::SpiDevice, }; -use crate::buffer_len; use crate::color::TriColor; use crate::interface::DisplayInterface; use crate::traits::{ InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay, }; +use crate::{buffer_len, color::Color}; pub(crate) mod command; use self::command::{ @@ -83,6 +86,29 @@ pub type Display2in13b = crate::graphics::Display< TriColor, >; +#[cfg(feature = "graphics")] +/// buffered buffer +pub type BufferMonoDisplay2in13b = crate::graphics::Display< + WIDTH, + { HEIGHT / BUFFER }, + false, + { buffer_len(WIDTH as usize, (HEIGHT / BUFFER) as usize) }, + Color, +>; + +#[cfg(feature = "graphics")] +/// buffered buffer +pub type BufferChromaticDisplay2in13b = crate::graphics::Display< + WIDTH, + { HEIGHT / BUFFER }, + false, + { buffer_len(WIDTH as usize, (HEIGHT / BUFFER) as usize) }, + TriColor, +>; + +/// buffers +pub const BUFFER: u32 = 4; + /// Width of the display. pub const WIDTH: u32 = 122; @@ -163,6 +189,79 @@ where } } +impl Epd2in13b +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// Transmit data to the SRAM of the EPD with the provided generators. + /// + /// Updates both the black and the secondary color layers + /// Useful for rendering directly from progmem buffers. + /// + /// Example: + /// ```rust no_run + /// progmem! { + /// static progmem BLACK: [u8; 4000] = *include_bytes!("black.gray"); + /// static progmem RED: [u8; 4000] = *include_bytes!("red.gray"); + /// } + /// epd.update_color_frame_with( + /// &mut spi, + /// &mut delay, + /// |i| BLACK.load_at(i), + /// |i| RED.load_at(i), + /// BLACK.len(), + /// RED.len(), + /// )?; + /// ``` + pub fn update_color_frame_with( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + black: impl Fn(usize) -> u8, + chromatic: impl Fn(usize) -> u8, + black_len: usize, + chromatic_len: usize, + ) -> Result<(), SPI::Error> { + self.update_achromatic_frame_with(spi, delay, black, black_len)?; + self.update_chromatic_frame_with(spi, delay, chromatic, chromatic_len) + } + + /// Update only the black/white data of the display using a generator + /// + /// This must be finished by calling `update_chromatic_frame`. + pub fn update_achromatic_frame_with( + &mut self, + spi: &mut SPI, + _delay: &mut DELAY, + black: impl Fn(usize) -> u8, + len: usize, + ) -> Result<(), SPI::Error> { + self.interface.cmd(spi, Command::WriteRam)?; + self.interface.data_with(spi, black, len)?; + Ok(()) + } + + /// Update only the chromatic data of the display. + /// + /// This should be preceded by a call to `update_achromatic_frame`. + /// This data takes precedence over the black/white data. + pub fn update_chromatic_frame_with( + &mut self, + spi: &mut SPI, + _delay: &mut DELAY, + chromatic: impl Fn(usize) -> u8, + len: usize, + ) -> Result<(), SPI::Error> { + self.interface.cmd(spi, Command::WriteRamRed)?; + self.interface.data_with(spi, chromatic, len)?; + Ok(()) + } +} + impl WaveshareThreeColorDisplay for Epd2in13b where @@ -327,6 +426,41 @@ where } } +#[derive(Copy, Clone)] +/// a type safe chunk reperesentation for BufferMonoDisplay +pub enum Chunk { + /// the first chunk of the display + Buf1, + /// the second chunk of the display + Buf2, + /// the third chunk of the display + Buf3, + /// the fourth chunk of the display + Buf4, +} + +impl Chunk { + /// converts chunk to a zero-indexed `u32` + pub fn to_zero_indexed(&self) -> u32 { + match self { + Chunk::Buf1 => 0, + Chunk::Buf2 => 1, + Chunk::Buf3 => 2, + Chunk::Buf4 => 3, + } + } + /// converts from zero-indexed `u32` to a `Chunk` + pub const fn from_zero_indexed(i: u32) -> Self { + match i { + 0 => Chunk::Buf1, + 1 => Chunk::Buf2, + 2 => Chunk::Buf3, + 3 => Chunk::Buf4, + _ => panic!("please check buffer"), + } + } +} + impl Epd2in13b where SPI: SpiDevice, @@ -335,6 +469,89 @@ where RST: OutputPin, DELAY: DelayNs, { + /// Due to memory limitations on the arduino boards, this function allows the user to separate the 122x250 board into four 122x62.5(rounding to 63) subgrids. + /// + /// for usage on `mono_buffers` and colored_buffers`, please refer to the documentation of `update_achromatic_buffered` and `update_chromatic_buffered` + pub fn update_frame_buffered( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + mono_buffers: impl FnMut(&mut BufferMonoDisplay2in13b, Chunk) -> Result, Infallible>, + colored_buffers: impl FnMut( + &mut BufferMonoDisplay2in13b, + Chunk, + ) -> Result, Infallible>, + ) -> Result<(), SPI::Error> { + self.update_achromatic_buffered(spi, delay, mono_buffers)?; + self.update_chromatic_buffered(spi, delay, colored_buffers) + } + + /// Due to memory limitations on the arduino boards, this function allows the user to separate the 122x250 board into four 122x62.5(rounding to 63) subgrids. + /// + /// IMPORTANT: this must be followed by `update_chromatic_buffered`, even if you're trying to only display purely mono content, otherwise the display won't be updated. + /// + /// `buffers`: A function that that should populate the content of each section of the display. + /// - Takes a mutable reference to `BuffermonoDisplay2in13b` and a buffer index(0-3) + /// - Returns `Result>` + /// * `Ok(Some(()))` indicates successful execution. + /// * `Ok(None)` indicates the buffer should be left unmodified, leaving it uncolored. + /// * `Err(Infalliable)` is here purely for allowing `?` with `embedded-graphics` draw operations. + pub fn update_achromatic_buffered( + &mut self, + spi: &mut SPI, + _delay: &mut DELAY, + mut buffers: impl FnMut(&mut BufferMonoDisplay2in13b, Chunk) -> Result, Infallible>, + ) -> Result<(), SPI::Error> { + self.interface.cmd(spi, Command::WriteRam)?; + for i in 0..BUFFER { + let mut buffer = BufferMonoDisplay2in13b::default(); + if buffers(&mut buffer, Chunk::from_zero_indexed(i)) + .unwrap() + .is_none() + { + buffer.clear(Color::White).unwrap(); + } + let data = buffer.buffer(); + self.interface.data(spi, data)?; + } + Ok(()) + } + + /// Due to memory limitations on the arduino boards, this function allows the user to separate the 122x250 board into four 122x62.5(rounding to 63) subgrids. + /// + /// IMPORTANT: this function must be called after `update_achromatic_buffered`, even if you're trying to only display purely mono content, otherwise the display won't be updated. + /// + /// The usage of color within `BufferMonoDisplay2in13b` is a misnomer. `Color::White` stands for colored(red), while `Color::Black` stands for uncolored(white). + /// + /// `buffers`: A function that that should populate the content of each section of the display. + /// - Takes a mutable reference to `BufferMonoDisplay2in13b` and a buffer index(0-3) + /// - Returns `Result>` + /// * `Ok(Some(()))` indicates successful execution + /// * `Ok(None)` indicates the buffer should be left unmodified, leaving it uncolored. + /// * `Err(Infalliable)` is here purely for allowing `?` with `embedded-graphics` draw operations. + pub fn update_chromatic_buffered( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + mut buffers: impl FnMut(&mut BufferMonoDisplay2in13b, Chunk) -> Result, Infallible>, + ) -> Result<(), SPI::Error> { + self.command(spi, Command::WriteRamRed)?; + let mut buffer = BufferMonoDisplay2in13b::default(); + for i in 0..BUFFER { + if buffers(&mut buffer, Chunk::from_zero_indexed(i)) + .unwrap() + .is_none() + { + buffer.clear(Color::Black).unwrap(); + } + let data = buffer.buffer(); + self.interface.data(spi, data)?; + } + self.interface.cmd(spi, Command::MasterActivation)?; + self.wait_until_idle(spi, delay)?; + Ok(()) + } + fn set_display_update_control( &mut self, spi: &mut SPI, diff --git a/src/interface.rs b/src/interface.rs index f3a9a3d7..25ef2c2a 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -76,6 +76,30 @@ where Ok(()) } + /// Basic function for sending u8-values with the provided generator. + /// + /// Intented for use with rendering from progmem buffers. + pub(crate) fn data_with( + &mut self, + spi: &mut SPI, + data: impl Fn(usize) -> u8, + len: usize, + ) -> Result<(), SPI::Error> { + // high for data + let _ = self.dc.set_high(); + + if SINGLE_BYTE_WRITE { + for i in 0..len { + // Transfer data one u8 at a time over spi + self.write(spi, &[data(i)])?; + } + } else { + unimplemented!(); + } + + Ok(()) + } + /// Basic function for sending [Commands](Command) and the data belonging to it. /// /// TODO: directly use ::write? cs wouldn't needed to be changed twice than