diff --git a/src/lib.rs b/src/lib.rs index b1ef66f..1682658 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,21 @@ #![no_std] +//! This library is an embedded-hal based driver implementation for the GDEQ031T10 e-paper display. +//! +//! **Note**: 9-bit SPI mode (where the data/command is designated with an extra bit for each byte) +//! is not supported by this driver. Only the mode with a dedicated data/command line is supported. + use embedded_hal::{ delay::DelayNs, - digital::{InputPin, OutputPin}, spi::{Operation, SpiDevice}, + digital::{InputPin, OutputPin}, + spi::{Operation, SpiDevice}, }; -const EPD_WIDTH: u32 = 240; -const EPD_HEIGHT: u32 = 320; -const EPD_ARRAY: u32 = EPD_WIDTH * EPD_HEIGHT / 8; +const EPD_WIDTH: usize = 240; +const EPD_HEIGHT: usize = 320; +const EPD_ARRAY: usize = EPD_WIDTH * EPD_HEIGHT / 8; +/// The display driver for the GDEQ031T10 e-paper display. pub struct EPaperDisplay { busy: BusyPin, reset: ResetPin, @@ -17,23 +24,176 @@ pub struct EPaperDisplay { delay: Delay, } +/// An error that may occur during display initialisation. pub enum InitError { + /// This variant is for any error with setting the level of the reset pin. + /// + /// This variant may be uninhabited if the `ResetPin` type parameter for [EPaperDisplay] is one that + /// doesn't produce errors. ResetError(ResetError), + /// This variant is for errors when attempting to communicate with the display controller after reset. WriteError(WriteError), } +/// An error when trying to communicate with the display controller. pub enum WriteError { + /// This variant is for any error with setting the level of the DC (Data/Command) pin. + /// + /// This variant may be uninhabited if the `DcPin` type parameter for [EPaperDisplay] is one that + /// doesn't produce errors. DcError(DcError), + /// This variant is for errors produced by the SPI driver. SpiError(SpiError), } -impl< - BusyPin: InputPin, - ResetPin: OutputPin, - DcPin: OutputPin, - Spi: SpiDevice, - Delay: DelayNs, -> EPaperDisplay +pub struct Frame([u8; EPD_ARRAY]); + +impl Frame { + pub const fn new_white() -> Self { + Self([0xff; EPD_ARRAY]) + } + + pub const fn new_black() -> Self { + Self([0x00; EPD_ARRAY]) + } +} + +pub struct DoubleFrame { + old: Frame, + new: Frame, + partial_window: PartialWindow, +} + +pub enum PixelColour { + Black = 0, + White = 1, +} + +pub trait ApplyParitalWindow: private::Sealed { + fn apply< + BusyPin: InputPin, + ResetPin: OutputPin, + DcPin: OutputPin, + Spi: SpiDevice, + Delay: DelayNs, + >( + &self, + display: &mut EPaperDisplay, + ); + + fn update_partial_window(&mut self, min_x: u16, max_x: u16, min_y: u16, max_y: u16); + + fn reset_partial_window(&mut self); +} + +mod private { + pub(super) trait Sealed {} + + impl Sealed for super::NoPartialWindow {} + + impl Sealed for super::AutomaticPartialWindow {} +} + +pub struct NoPartialWindow; + +impl ApplyParitalWindow for NoPartialWindow { + fn apply< + BusyPin: InputPin, + ResetPin: OutputPin, + DcPin: OutputPin, + Spi: SpiDevice, + Delay: DelayNs, + >( + &self, + _display: &mut EPaperDisplay, + ) { + } + + fn update_partial_window(&mut self, _min_x: u16, _max_x: u16, _min_y: u16, _max_y: u16) {} + + fn reset_partial_window(&mut self) {} +} + +pub struct AutomaticPartialWindow { + min_x: u16, + max_x: u16, + min_y: u16, + max_y: u16, +} + +impl ApplyParitalWindow for AutomaticPartialWindow { + fn apply< + BusyPin: InputPin, + ResetPin: OutputPin, + DcPin: OutputPin, + Spi: SpiDevice, + Delay: DelayNs, + >( + &self, + display: &mut EPaperDisplay, + ) { + let hrst = self.min_x as u8 & 0b11111000; + let hred = self.max_x as u8 | 0b111; + let vrst = self.min_y & 0x1FF; + let vred = self.max_y & 0x1FF; + display.write_command(0x90); + display.write_data(hrst); + display.write_data(hred); + display.write_data((vrst >> 8) as u8); + display.write_data((vrst & 0xFF) as u8); + display.write_data((vred >> 8) as u8); + display.write_data((vred & 0xFF) as u8); + display.write_data(0x00); + } + + fn update_partial_window(&mut self, min_x: u16, max_x: u16, min_y: u16, max_y: u16) { + self.min_x = core::cmp::min(self.min_x, min_x); + self.max_x = core::cmp::max(self.max_x, max_x); + self.min_y = core::cmp::min(self.min_y, min_y); + self.max_y = core::cmp::max(self.max_y, max_y); + } + + fn reset_partial_window(&mut self) { + *self = AutomaticPartialWindow { + min_x: EPD_WIDTH as u16, + max_x: 0, + min_y: EPD_HEIGHT as u16, + max_y: 0, + } + } +} + +impl DoubleFrame { + pub fn new(old: Frame, new: Frame) -> Self { + Self { + old, + new, + partial_window: NoPartialWindow, + } + } + + pub fn draw_pixel(&mut self, x: usize, y: usize, colour: PixelColour) { + todo!() + } +} + +impl DoubleFrame { + pub fn new_with_automatic_partial_window(old: Frame, new: Frame) -> Self { + Self { + old, + new, + partial_window: AutomaticPartialWindow { + min_x: EPD_WIDTH as u16, + max_x: 0, + min_y: EPD_HEIGHT as u16, + max_y: 0, + }, + } + } +} + +impl + EPaperDisplay { pub fn new( busy: BusyPin, @@ -54,22 +214,26 @@ impl< fn write_command(&mut self, byte: u8) -> Result<(), WriteError> { self.dc.set_low().map_err(WriteError::DcError)?; - self.spi.transaction(&mut [ - Operation::DelayNs(60), - Operation::Write(&[byte]), - Operation::DelayNs(20), - ]).map_err(WriteError::SpiError)?; + self.spi + .transaction(&mut [ + Operation::DelayNs(60), + Operation::Write(&[byte]), + Operation::DelayNs(20), + ]) + .map_err(WriteError::SpiError)?; self.delay.delay_ns(40); Ok(()) } fn write_data(&mut self, byte: u8) -> Result<(), WriteError> { self.dc.set_high().map_err(WriteError::DcError)?; - self.spi.transaction(&mut [ - Operation::DelayNs(60), - Operation::Write(&[byte]), - Operation::DelayNs(20), - ]).map_err(WriteError::SpiError)?; + self.spi + .transaction(&mut [ + Operation::DelayNs(60), + Operation::Write(&[byte]), + Operation::DelayNs(20), + ]) + .map_err(WriteError::SpiError)?; self.delay.delay_ns(40); Ok(()) } @@ -107,4 +271,39 @@ impl< self.delay.delay_ms(1); self.wait_for_display(); } + + pub fn draw_full_frame(&mut self, double_frame: &mut DoubleFrame) { + double_frame.partial_window.apply(self); + self.write_command(0x10); + for byte in double_frame.old.0 { + self.write_data(byte); + } + self.write_command(0x13); + for byte in double_frame.new.0 { + self.write_data(byte); + } + self.write_command(0x12); + double_frame.old.0 = double_frame.new.0; + + self.delay.delay_ms(1); + self.wait_for_display(); + } + + /// Puts the display into deep sleep mode which effectively deinitialises the display controller. + /// + /// Reinitialising the display with [EPaperDisplay::new] is sufficient to wake the display back up. + /// + /// All display controller registers will be reset by this process. + pub fn deep_sleep(mut self) { + self.write_command(0x07); + self.write_data(0xA5); + } + + pub fn enable_partial_mode(&mut self) { + self.write_command(0x91); + } + + pub fn disable_partial_mode(&mut self) { + self.write_command(0x92); + } }