#![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}, }; 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, dc: DcPin, spi: SpiDevice, 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), } 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 ApplyPartialWindow: 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 ApplyPartialWindow 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 ApplyPartialWindow 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, } } } 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 DoubleFrame { pub fn draw_pixel(&mut self, x: u16, y: u16, colour: PixelColour) { todo!() } } impl EPaperDisplay { pub fn new( busy: BusyPin, reset: ResetPin, dc: DcPin, spi: Spi, delay: Delay, ) -> Result> { let mut display = Self { busy, reset, dc, spi, delay, }; display.init().map(|_| display) } 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.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.delay.delay_ns(40); Ok(()) } fn wait_for_display(&mut self) { while self.busy.is_low().expect("Failed to read busy pin") {} } fn init(&mut self) -> Result<(), InitError> { self.reset.set_low().map_err(InitError::ResetError)?; self.delay.delay_ms(10); self.reset.set_high().map_err(InitError::ResetError)?; self.delay.delay_ms(10); self.write_command(0x00).map_err(InitError::WriteError)?; self.write_data(0x1f).map_err(InitError::WriteError)?; self.write_command(0x04).map_err(InitError::WriteError)?; self.wait_for_display(); Ok(()) } fn clear_display(&mut self) { self.write_command(0x10); for _ in 0..EPD_ARRAY { self.write_data(0xff); } self.write_command(0x13); for _ in 0..EPD_ARRAY { self.write_data(0xff); } self.write_command(0x12); 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); } }