From f30e0b7dc4d7a9e44f940eaaae0c4d5587c46610 Mon Sep 17 00:00:00 2001 From: Zac Wilson Date: Mon, 15 Dec 2025 11:59:07 +0000 Subject: [PATCH 1/4] Started writing frame drawing --- src/lib.rs | 90 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b1ef66f..8ef85bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,13 @@ 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; pub struct EPaperDisplay { busy: BusyPin, @@ -27,13 +28,40 @@ pub enum WriteError { 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, +} + +pub enum PixelColour { + Black = 0, + White = 1, +} + +impl DoubleFrame { + pub fn new(old: Frame, new: Frame) -> Self { + Self { old, new } + } + + pub fn draw_pixel(&mut self, x: usize, y: usize, colour: PixelColour) { + + } +} + +impl + EPaperDisplay { pub fn new( busy: BusyPin, @@ -54,22 +82,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 +139,20 @@ impl< self.delay.delay_ms(1); self.wait_for_display(); } + + pub fn draw_full_frame(&mut self, double_frame: &mut DoubleFrame) { + 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(); + } } From fa4cc5d3f2a049dbc4a8970b2b3fea8f5474f565 Mon Sep 17 00:00:00 2001 From: Zac Wilson Date: Mon, 15 Dec 2025 16:19:30 +0000 Subject: [PATCH 2/4] Started deep sleep command --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8ef85bb..4cb890e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,4 +155,14 @@ impl Date: Mon, 15 Dec 2025 16:56:34 +0000 Subject: [PATCH 3/4] Started writing documentation --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4cb890e..ef014a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ #![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}, @@ -10,6 +15,7 @@ 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, @@ -18,13 +24,25 @@ 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), } From 05e927d0a8ff47f2ae43045d03034c1426cc3d23 Mon Sep 17 00:00:00 2001 From: Zac Wilson Date: Thu, 18 Dec 2025 10:24:33 +0000 Subject: [PATCH 4/4] Added support for partial window tracking --- src/lib.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef014a2..1682658 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![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. @@ -27,7 +27,7 @@ pub struct EPaperDisplay { /// 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), @@ -38,7 +38,7 @@ pub enum InitError { /// 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), @@ -58,9 +58,10 @@ impl Frame { } } -pub struct DoubleFrame { +pub struct DoubleFrame { old: Frame, new: Frame, + partial_window: PartialWindow, } pub enum PixelColour { @@ -68,13 +69,126 @@ pub enum PixelColour { White = 1, } -impl DoubleFrame { +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 } + 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, + }, + } } } @@ -158,7 +272,8 @@ impl(&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); @@ -175,12 +290,20 @@ impl