From 6c27bb4369c1338bcb1b75796102aac8ca7c5fbe Mon Sep 17 00:00:00 2001 From: pbert519 Date: Wed, 19 Jun 2024 17:10:49 +0200 Subject: [PATCH] Support configuration of timeouts and buffer size --- README.md | 7 ++---- examples/esp32/src/main.rs | 3 ++- examples/test_eink.rs | 3 ++- src/area_serializer.rs | 29 +++++++++------------- src/interface.rs | 10 ++++++++ src/lib.rs | 51 ++++++++++++++++++++++++++++++++++---- src/pixel_serializer.rs | 40 +++++++++++------------------- 7 files changed, 88 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index a912c58..9ef684d 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,12 @@ This driver can be used with the embedded graphics trait, currently only suppori - Support display engine fill area - Support display engine 1 bit per pixel mode - Support static buffer allocations -- Make max buffer size configurable (default = 1kByte) -- Make gpio busy timeout configurable (default = 10s) -- Make lut timeout configurable (default = 10s) - ## Changelog ### 0.4.0 +- **Public API** `new` expects a `Config` parameter to set timeout and buffer size. Default is implemented with timeouts of 15s and buffer size is 1024 Bytes. - Buffer data type changed from u16 to u8 - **Public API**: `load_image_area`, `load_image`, and `memory_burst_write` functions are now using u8 as buffer type - Memory usage is reduced by half (1kByte max. instead of 2kByte) -- **Behavior** Calling `init` no longer clears the eink display. Instead call `reset` directly. +- **Behavior** Calling `init` no longer clears the eink display. Instead call `reset` directly. diff --git a/examples/esp32/src/main.rs b/examples/esp32/src/main.rs index 7a6959d..c299ff2 100644 --- a/examples/esp32/src/main.rs +++ b/examples/esp32/src/main.rs @@ -2,6 +2,7 @@ use esp_idf_hal::{delay::Ets, gpio::PinDriver, prelude::*, spi::*}; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use it8951::{interface::*, *}; use embedded_graphics::{prelude::*, primitives::{Rectangle, PrimitiveStyle}, pixelcolor::Gray4}; +use it8951::Config; fn main() -> ! { // Bind the log crate to the ESP Logging facilities @@ -33,7 +34,7 @@ fn main() -> ! { reset, Ets, ); - let mut epd = IT8951::new(display_interface).init(1605).unwrap(); + let mut epd = IT8951::new(display_interface, Config::default()).init(1605).unwrap(); log::info!("Initialized display: {:?}", epd.get_dev_info()); diff --git a/examples/test_eink.rs b/examples/test_eink.rs index fa68c59..8cfa475 100644 --- a/examples/test_eink.rs +++ b/examples/test_eink.rs @@ -1,3 +1,4 @@ +use it8951::Config; use linux_embedded_hal::gpio_cdev::{Chip, LineRequestFlags}; use linux_embedded_hal::spidev::{SpiModeFlags, SpidevOptions}; use linux_embedded_hal::{CdevPin, Delay, SpidevDevice}; @@ -34,7 +35,7 @@ fn main() -> Result<(), Box> { let busy = CdevPin::new(busy_input_handle)?; let driver = it8951::interface::IT8951SPIInterface::new(spi, busy, rst, Delay); - let mut epd = it8951::IT8951::new(driver).init(1670).unwrap(); + let mut epd = it8951::IT8951::new(driver, Config::default()).init(1670).unwrap(); println!( "Reset and initalized E-Ink Display: \n\r {:?}", diff --git a/src/area_serializer.rs b/src/area_serializer.rs index a22323f..b00c790 100644 --- a/src/area_serializer.rs +++ b/src/area_serializer.rs @@ -13,12 +13,7 @@ pub struct AreaSerializer { } impl AreaSerializer { - pub fn new(area: Rectangle, color: Gray4) -> Self { - let max_entries: usize = 1024; // 1 KByte - - Self::with_buffer_size(area, color, max_entries) - } - pub fn with_buffer_size(area: Rectangle, color: Gray4, buffer_size: usize) -> Self { + pub fn new(area: Rectangle, color: Gray4, buffer_size: usize) -> Self { let raw_color = color.luma(); let data_entry = raw_color << 4 | raw_color; @@ -98,7 +93,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -125,7 +120,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -151,7 +146,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -177,7 +172,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -204,7 +199,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( @@ -232,7 +227,7 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -259,7 +254,7 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::with_buffer_size( + let area_s = AreaSerializer::new( area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 2, @@ -302,7 +297,7 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::with_buffer_size( + let area_s = AreaSerializer::new( area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 4, @@ -345,7 +340,7 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -372,7 +367,7 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -399,7 +394,7 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 1024); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), diff --git a/src/interface.rs b/src/interface.rs index f691651..7ea3dab 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -22,6 +22,10 @@ pub enum Error { /// Trait to describe the interface with the controller /// The controller supports different hardware interfaces like i2c, usb, spi and i80 pub trait IT8951Interface { + /// set wait timeout + /// internally used by the library + fn set_busy_timeout(&mut self, timeout: core::time::Duration); + /// active wait while the controller is busy and no new transactions should be issued fn wait_while_busy(&mut self) -> Result<(), Error>; @@ -64,6 +68,7 @@ pub struct IT8951SPIInterface { busy: BUSY, rst: RST, delay: DELAY, + timeout: core::time::Duration } impl IT8951SPIInterface @@ -85,6 +90,7 @@ where busy, rst, delay, + timeout: core::time::Duration::default() } } } @@ -96,6 +102,10 @@ where RST: OutputPin, DELAY: DelayNs, { + fn set_busy_timeout(&mut self, timeout: core::time::Duration) { + self.timeout = timeout + } + fn wait_while_busy(&mut self) -> Result<(), Error> { let mut counter = 0u64; while self.busy.is_low().map_err(|_| Error::GPIOError)? { diff --git a/src/lib.rs b/src/lib.rs index fc7ae92..5f29282 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,29 @@ impl From for Error { } } +/// Driver configuration +pub struct Config { + /// Timeout for the internal display engine + pub timeout_display_engine: core::time::Duration, + /// Timeout for the busy pin + pub timeout_interface: core::time::Duration, + /// Max buffer size in bytes for staging buffers + /// The buffer should be large enough to at least contain the pixels of a complete row + /// The buffer must be aligned to u16 + /// The used IT8951 interface must support to write a complete buffer at once + pub max_buffer_size: usize, +} + +impl Default for Config { + fn default() -> Self { + Self { + timeout_display_engine: core::time::Duration::from_secs(15), + timeout_interface: core::time::Duration::from_secs(15), + max_buffer_size: 1024, + } + } +} + /// Device Info Struct /// Describes the connected display #[derive(Debug, Clone)] @@ -103,16 +126,19 @@ pub struct IT8951 { interface: IT8951Interface, dev_info: Option, marker: core::marker::PhantomData, + config: Config, } impl IT8951 { /// Creates a new controller driver object /// Call init afterwards to initalize the controller - pub fn new(interface: IT8951Interface) -> Self { + pub fn new(mut interface: IT8951Interface, config: Config) -> Self { + interface.set_busy_timeout(config.timeout_interface); IT8951 { interface, dev_info: None, marker: PhantomData {}, + config, } } @@ -126,6 +152,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, } .sys_run()?; @@ -145,11 +172,17 @@ impl IT8951 { /// Create a new Driver for are already active and initalized driver /// This can be usefull if the device was still powered on, but the uC restarts. - pub fn attach(interface: IT8951Interface) -> Result, Error> { + pub fn attach( + mut interface: IT8951Interface, + config: Config, + ) -> Result, Error> { + interface.set_busy_timeout(config.timeout_interface); + let mut it8951 = IT8951 { interface, dev_info: None, marker: PhantomData {}, + config, } .sys_run()?; @@ -358,9 +391,10 @@ impl IT8951 { // misc ------------------------------------------------------------------------------------------------ fn wait_for_display_ready(&mut self) -> Result<(), Error> { + let timeout = self.config.timeout_display_engine.as_micros() as u64; let mut counter = 0u64; while 0 != self.read_register(register::LUTAFSR)? { - if counter > 10_000_000u64 { + if counter > timeout { return Err(Error::DisplayEngineTimeout); } counter += 1; @@ -377,6 +411,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, }) } @@ -388,6 +423,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, }) } @@ -462,6 +498,7 @@ impl IT8951 DrawTarget for IT8951 Result<(), Self::Error> { - let a = AreaSerializer::new(*area, color); + let a = AreaSerializer::new(*area, color, self.config.max_buffer_size); let area_iter = AreaSerializerIterator::new(&a); for (area_img_info, buffer) in area_iter { @@ -512,7 +549,11 @@ impl DrawTarget for IT8951>> { } impl>> PixelSerializer { - pub fn new(area: Rectangle, pixels: I) -> Self { + pub fn new(area: Rectangle, pixels: I, size: usize) -> Self { PixelSerializer { area, pixels, row: 0, // 1kByte - max_entries: 1024, - } - } - // max buffer size in bytes - // must be aligned to u16! - // TODO make variable buffer size available via public api - #[allow(unused)] - pub fn with_buffer_max_words(self, size: usize) -> Self { - assert!(size % 2 == 0, "Buffer size must be aligned to u16"); - Self { max_entries: size, - ..self } } } @@ -137,6 +126,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xF)].into_iter(), ), + 1024 ); assert_eq!( s.next(), @@ -169,7 +159,7 @@ mod tests { area, BOUNDING_BOX_DEFAULT, vec![Gray4::new(0x1)].into_iter(), - ), + ),1024 ); assert_eq!( s.next(), @@ -201,7 +191,7 @@ mod tests { area, BOUNDING_BOX_DEFAULT, vec![Gray4::new(0x4)].into_iter(), - ), + ),1024 ); assert_eq!( s.next(), @@ -233,7 +223,7 @@ mod tests { area, BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xC)].into_iter(), - ), + ),1024 ); assert_eq!( s.next(), @@ -272,7 +262,7 @@ mod tests { Gray4::new(0xD), ] .into_iter(), - ), + ),1024 ); assert_eq!( s.next(), @@ -305,7 +295,7 @@ mod tests { area, BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xC), Gray4::new(0xD), Gray4::new(0xE)].into_iter(), - ), + ),1024 ); assert_eq!( s.next(), @@ -348,9 +338,8 @@ mod tests { Gray4::new(0x4), ] .into_iter(), - ), - ) - .with_buffer_max_words(2); + ),2 + ); assert_eq!( s.next(), Some(( @@ -402,9 +391,8 @@ mod tests { Gray4::new(0x3), ] .into_iter(), - ), - ) - .with_buffer_max_words(4); + ), 4 + ); assert_eq!( s.next(), Some(( @@ -458,7 +446,7 @@ mod tests { Gray4::new(0x4), ] .into_iter(), - ), + ), 1024 ); assert_eq!( s.next(), @@ -499,7 +487,7 @@ mod tests { Gray4::new(0x3), ] .into_iter(), - ), + ), 1024 ); assert_eq!( s.next(), @@ -540,7 +528,7 @@ mod tests { Gray4::new(0x3), ] .into_iter(), - ), + ), 1024 ); assert_eq!( s.next(),