From 0a812168d52dd040beb88e0fafe8633b10a720ad Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 15 Nov 2024 10:23:14 -0600 Subject: [PATCH 01/36] WIP --- mipidsi/Cargo.toml | 3 - mipidsi/src/batch.rs | 11 +- mipidsi/src/builder.rs | 19 +- mipidsi/src/dcs.rs | 20 +- mipidsi/src/error.rs | 22 +- mipidsi/src/graphics.rs | 20 +- mipidsi/src/interface/mod.rs | 38 ++++ mipidsi/src/interface/parallel.rs | 341 ++++++++++++++++++++++++++++++ mipidsi/src/interface/spi.rs | 156 ++++++++++++++ mipidsi/src/lib.rs | 129 ++++------- mipidsi/src/models.rs | 15 +- mipidsi/src/models/gc9a01.rs | 24 +-- mipidsi/src/models/ili9341.rs | 27 +-- mipidsi/src/models/ili9342c.rs | 27 +-- mipidsi/src/models/ili934x.rs | 39 +--- mipidsi/src/models/ili9486.rs | 51 +---- mipidsi/src/models/st7735s.rs | 24 +-- mipidsi/src/models/st7789.rs | 25 +-- mipidsi/src/models/st7796.rs | 15 +- mipidsi/tests/external.rs | 25 +-- 20 files changed, 665 insertions(+), 366 deletions(-) create mode 100644 mipidsi/src/interface/mod.rs create mode 100644 mipidsi/src/interface/parallel.rs create mode 100644 mipidsi/src/interface/spi.rs diff --git a/mipidsi/Cargo.toml b/mipidsi/Cargo.toml index 8c6d1f8..1eecc47 100644 --- a/mipidsi/Cargo.toml +++ b/mipidsi/Cargo.toml @@ -12,7 +12,6 @@ documentation = "https://docs.rs/mipidsi" rust-version = "1.75" [dependencies] -display-interface = "0.5.0" embedded-graphics-core = "0.4.0" embedded-hal = "1.0.0" nb = "1.0.0" @@ -23,8 +22,6 @@ version = "0.8.0" [dev-dependencies] embedded-graphics = "0.8.0" -display-interface-spi = "0.5.0" -display-interface-parallel-gpio = "0.7.0" [features] default = ["batch"] diff --git a/mipidsi/src/batch.rs b/mipidsi/src/batch.rs index 9046f1f..a04f8e9 100644 --- a/mipidsi/src/batch.rs +++ b/mipidsi/src/batch.rs @@ -1,28 +1,27 @@ //! Original code from: [this repo](https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs) //! Batch the pixels to be rendered into Pixel Rows and Pixel Blocks (contiguous Pixel Rows). //! This enables the pixels to be rendered efficiently as Pixel Blocks, which may be transmitted in a single Non-Blocking SPI request. -use crate::{error::Error, models::Model, Display}; -use display_interface::WriteOnlyDataCommand; +use crate::{interface::PixelInterface, models::Model, Display}; use embedded_graphics_core::prelude::*; use embedded_hal::digital::OutputPin; pub trait DrawBatch where - DI: WriteOnlyDataCommand, + DI: PixelInterface, M: Model, I: IntoIterator>, { - fn draw_batch(&mut self, item_pixels: I) -> Result<(), Error>; + fn draw_batch(&mut self, item_pixels: I) -> Result<(), DI::Error>; } impl DrawBatch for Display where - DI: WriteOnlyDataCommand, + DI: PixelInterface, M: Model, I: IntoIterator>, RST: OutputPin, { - fn draw_batch(&mut self, item_pixels: I) -> Result<(), Error> { + fn draw_batch(&mut self, item_pixels: I) -> Result<(), DI::Error> { // Get the pixels for the item to be rendered. let pixels = item_pixels.into_iter(); // Batch the pixels into Pixel Rows. diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index f34b619..f62e09f 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -1,9 +1,9 @@ //! [super::Display] builder module -use display_interface::WriteOnlyDataCommand; use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; +use crate::interface::PixelInterface; use crate::{dcs::Dcs, error::InitError, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -28,7 +28,7 @@ use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, Refr /// ``` pub struct Builder where - DI: WriteOnlyDataCommand, + DI: PixelInterface, MODEL: Model, { di: DI, @@ -39,7 +39,7 @@ where impl Builder where - DI: WriteOnlyDataCommand, + DI: PixelInterface, MODEL: Model, { /// @@ -58,7 +58,7 @@ where impl Builder where - DI: WriteOnlyDataCommand, + DI: PixelInterface, MODEL: Model, RST: OutputPin, { @@ -149,7 +149,7 @@ where pub fn init( mut self, delay_source: &mut impl DelayNs, - ) -> Result, InitError> { + ) -> Result, InitError> { let to_u32 = |(a, b)| (u32::from(a), u32::from(b)); let (width, height) = to_u32(self.options.display_size); let (offset_x, offset_y) = to_u32(self.options.display_offset); @@ -165,10 +165,15 @@ where delay_source.delay_us(10); rst.set_high().map_err(InitError::Pin)?; } - None => dcs.write_command(crate::dcs::SoftReset)?, + None => dcs + .write_command(crate::dcs::SoftReset) + .map_err(InitError::DisplayError)?, } - let madctl = self.model.init(&mut dcs, delay_source, &self.options)?; + let madctl = self + .model + .init(&mut dcs, delay_source, &self.options) + .map_err(InitError::DisplayError)?; let display = Display { dcs, diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index 5756b79..7189437 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -1,8 +1,6 @@ //! MIPI DCS commands. -use display_interface::{DataFormat, WriteOnlyDataCommand}; - -use crate::error::Error; +use crate::interface::CommandInterface; #[macro_use] mod macros; @@ -35,7 +33,7 @@ pub trait DcsCommand { fn fill_params_buf(&self, buffer: &mut [u8]) -> usize; } -/// Wrapper around [`WriteOnlyDataCommand`] with support for writing DCS commands. +/// Wrapper around [`CommandInterface`] with support for writing DCS commands. /// /// Commands which are part of the manufacturer independent user command set can be sent to the /// display by using the [`write_command`](Self::write_command) method with one of the command types @@ -51,7 +49,7 @@ pub struct Dcs { impl Dcs where - DI: WriteOnlyDataCommand, + DI: CommandInterface, { /// Creates a new [Dcs] instance from a display interface. pub fn write_only(di: DI) -> Self { @@ -64,7 +62,7 @@ where } /// Sends a DCS command to the display interface. - pub fn write_command(&mut self, command: impl DcsCommand) -> Result<(), Error> { + pub fn write_command(&mut self, command: impl DcsCommand) -> Result<(), DI::Error> { let mut param_bytes: [u8; 16] = [0; 16]; let n = command.fill_params_buf(&mut param_bytes); self.write_raw(command.instruction(), ¶m_bytes[..n]) @@ -79,13 +77,9 @@ where /// This method is intended to be used for sending commands which are not part of the MIPI DCS /// user command set. Use [`write_command`](Self::write_command) for commands in the user /// command set. - pub fn write_raw(&mut self, instruction: u8, param_bytes: &[u8]) -> Result<(), Error> { - self.di.send_commands(DataFormat::U8(&[instruction]))?; - - if !param_bytes.is_empty() { - self.di.send_data(DataFormat::U8(param_bytes))?; // TODO: empty guard? - } - Ok(()) + pub fn write_raw(&mut self, instruction: u8, param_bytes: &[u8]) -> Result<(), DI::Error> { + self.di.send_command(instruction, param_bytes)?; + self.di.flush() } } diff --git a/mipidsi/src/error.rs b/mipidsi/src/error.rs index 758b9f1..143b20b 100644 --- a/mipidsi/src/error.rs +++ b/mipidsi/src/error.rs @@ -1,24 +1,10 @@ -//! [Error] module for [super::Display] - -use display_interface::DisplayError; +//! Error module for [super::Display] /// Error returned by [`Builder::init`](crate::Builder). #[derive(Debug)] -pub enum InitError { +pub enum InitError { /// Error caused by the display interface. - DisplayError, + DisplayError(DI), /// Error caused by the reset pin's [`OutputPin`](embedded_hal::digital::OutputPin) implementation. - Pin(PE), -} - -/// -/// Alias of [DisplayError] for out-of-init use cases -/// since the pin error is only possible during [super::Builder] use -/// -pub type Error = DisplayError; - -impl From for InitError { - fn from(_: DisplayError) -> Self { - InitError::DisplayError - } + Pin(P), } diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 57d26f4..2b3d81a 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -7,18 +7,17 @@ use embedded_graphics_core::{ }; use embedded_hal::digital::OutputPin; -use crate::dcs::BitsPerPixel; -use crate::models::Model; -use crate::{error::Error, Display}; -use display_interface::WriteOnlyDataCommand; +use crate::Display; +use crate::{dcs::BitsPerPixel, interface::PixelInterface}; +use crate::{dcs::WriteMemoryStart, models::Model}; impl DrawTarget for Display where - DI: WriteOnlyDataCommand, + DI: PixelInterface, M: Model, RST: OutputPin, { - type Error = Error; + type Error = DI::Error; type Color = M::ColorFormat; #[cfg(not(feature = "batch"))] @@ -104,19 +103,22 @@ where }; let count = area.size.width * area.size.height; - let mut colors = take_u32(core::iter::repeat(color), count); let sx = area.top_left.x as u16; let sy = area.top_left.y as u16; let ex = bottom_right.x as u16; let ey = bottom_right.y as u16; - self.set_pixels(sx, sy, ex, ey, &mut colors) + + self.set_address_window(sx, sy, ex, ey)?; + self.dcs.write_command(WriteMemoryStart)?; + self.dcs.di.send_repeated_pixel(color, count)?; + self.dcs.di.flush() } } impl OriginDimensions for Display where - DI: WriteOnlyDataCommand, + DI: PixelInterface, MODEL: Model, RST: OutputPin, { diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs new file mode 100644 index 0000000..ce00954 --- /dev/null +++ b/mipidsi/src/interface/mod.rs @@ -0,0 +1,38 @@ +//! Interface traits and implementations + +/// Command interface +pub trait CommandInterface { + /// Error type + type Error: core::fmt::Debug; + + /// Send a command with optional parameters + /// + /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error>; + + /// Sends any remaining buffered data + fn flush(&mut self) -> Result<(), Self::Error>; +} + +/// Pixel interface +pub trait PixelInterface: CommandInterface { + /// Send a single pixel + /// + /// `WriteMemoryStart` must be sent before calling this function + /// + /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + fn send_pixel(&mut self, pixel: P) -> Result<(), Self::Error>; + + /// Send the same pixel value multiple times + /// + /// `WriteMemoryStart` must be sent before calling this function + /// + /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + fn send_repeated_pixel(&mut self, pixel: P, count: u32) -> Result<(), Self::Error>; +} + +mod spi; +pub use spi::*; + +mod parallel; +pub use parallel::*; diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs new file mode 100644 index 0000000..3064b8c --- /dev/null +++ b/mipidsi/src/interface/parallel.rs @@ -0,0 +1,341 @@ +use embedded_graphics_core::pixelcolor::{raw::ToBytes, Rgb565}; +use embedded_hal::digital::OutputPin; + +use super::{CommandInterface, PixelInterface}; + +/// This trait represents the data pins of a parallel bus. +/// +/// See [Generic8BitBus] and [Generic16BitBus] for generic implementations. +pub trait OutputBus { + /// [u8] for 8-bit buses, [u16] for 16-bit buses, etc. + type Word: Copy; + + /// Error type + type Error: core::fmt::Debug; + + /// Set the output bus to a specific value + fn set_value(&mut self, value: Self::Word) -> Result<(), Self::Error>; +} + +macro_rules! generic_bus { + ($GenericxBitBus:ident { type Word = $Word:ident; Pins {$($PX:ident => $x:tt,)*}}) => { + /// A generic implementation of [OutputBus] using [OutputPin]s + pub struct $GenericxBitBus<$($PX, )*> { + pins: ($($PX, )*), + last: Option<$Word>, + } + + impl<$($PX, )*> $GenericxBitBus<$($PX, )*> + where + $($PX: OutputPin, )* + { + /// Creates a new bus. This does not change the state of the pins. + /// + /// The first pin in the tuple is the least significant bit. + pub fn new(pins: ($($PX, )*)) -> Self { + Self { pins, last: None } + } + + /// Consumes the bus and returns the pins. This does not change the state of the pins. + pub fn release(self) -> ($($PX, )*) { + self.pins + } + } + + impl<$($PX, )* E> OutputBus + for $GenericxBitBus<$($PX, )*> + where + $($PX: OutputPin, )* + E: core::fmt::Debug, + { + type Word = $Word; + type Error = E; + + fn set_value(&mut self, value: Self::Word) -> Result<(), Self::Error> { + if self.last == Some(value) { + // It's quite common for multiple consecutive values to be identical, e.g. when filling or + // clearing the screen, so let's optimize for that case + return Ok(()) + } + + // Sets self.last to None. + // We will update it to Some(value) *after* all the pins are succesfully set. + let last = self.last.take(); + + let changed = match last { + Some(old_value) => value ^ old_value, + None => !0, // all ones, this ensures that we will update all the pins + }; + + $( + let mask = 1 << $x; + if changed & mask != 0 { + if value & mask != 0 { + self.pins.$x.set_high() + } else { + self.pins.$x.set_low() + } + ?; + } + )* + + self.last = Some(value); + Ok(()) + } + } + + impl<$($PX, )*> From<($($PX, )*)> + for $GenericxBitBus<$($PX, )*> + where + $($PX: OutputPin, )* + { + fn from(pins: ($($PX, )*)) -> Self { + Self::new(pins) + } + } + }; +} + +generic_bus! { + Generic8BitBus { + type Word = u8; + Pins { + P0 => 0, + P1 => 1, + P2 => 2, + P3 => 3, + P4 => 4, + P5 => 5, + P6 => 6, + P7 => 7, + } + } +} + +generic_bus! { + Generic16BitBus { + type Word = u16; + Pins { + P0 => 0, + P1 => 1, + P2 => 2, + P3 => 3, + P4 => 4, + P5 => 5, + P6 => 6, + P7 => 7, + P8 => 8, + P9 => 9, + P10 => 10, + P11 => 11, + P12 => 12, + P13 => 13, + P14 => 14, + P15 => 15, + } + } +} + +/// Parallel interface error +#[derive(Clone, Copy, Debug)] +pub enum ParallelError { + /// Bus error + Bus(BUS), + /// Data/command pin error + Dc(DC), + /// Write pin error + Wr(WR), +} + +/// Parallel 8 Bit communication interface +/// +/// This interface implements an 8-Bit "8080" style write-only display interface using any +/// 8-bit [OutputBus] implementation as well as one +/// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. +/// +/// All pins are supposed to be high-active, high for the D/C pin meaning "data" and the +/// write-enable being pulled low before the setting of the bits and supposed to be sampled at a +/// low to high edge. +pub struct PGpio8BitInterface { + bus: BUS, + dc: DC, + wr: WR, +} + +impl PGpio8BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + /// Create new parallel GPIO interface for communication with a display driver + pub fn new(bus: BUS, dc: DC, wr: WR) -> Self { + Self { bus, dc, wr } + } + + /// Consume the display interface and return + /// the bus and GPIO pins used by it + pub fn release(self) -> (BUS, DC, WR) { + (self.bus, self.dc, self.wr) + } + + fn send_byte( + &mut self, + byte: u8, + ) -> Result<(), ParallelError> { + self.wr.set_low().map_err(ParallelError::Wr)?; + self.bus.set_value(byte).map_err(ParallelError::Bus)?; + self.wr.set_high().map_err(ParallelError::Wr) + } +} + +impl CommandInterface for PGpio8BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + type Error = ParallelError; + + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + self.dc.set_low().map_err(ParallelError::Dc)?; + self.send_byte(command)?; + self.dc.set_high().map_err(ParallelError::Dc)?; + + for arg in args { + self.send_byte(*arg)?; + } + + Ok(()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl PixelInterface for PGpio8BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { + for byte in pixel.to_be_bytes() { + self.send_byte(byte)?; + } + Ok(()) + } + + fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { + if count == 0 { + return Ok(()); + } + let [byte1, byte2] = pixel.to_be_bytes(); + if byte1 == byte2 { + self.send_byte(byte1)?; + for _ in 1..(count * 2) { + self.dc.set_low().map_err(ParallelError::Dc)?; + self.dc.set_high().map_err(ParallelError::Dc)?; + } + Ok(()) + } else { + for _ in 0..count { + self.send_pixel(pixel)?; + } + Ok(()) + } + } +} + +/// Parallel 16 Bit communication interface +/// +/// This interface implements a 16-Bit "8080" style write-only display interface using any +/// 16-bit [OutputBus] implementation as well as one +/// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. +/// +/// All pins are supposed to be high-active, high for the D/C pin meaning "data" and the +/// write-enable being pulled low before the setting of the bits and supposed to be sampled at a +/// low to high edge. +pub struct PGpio16BitInterface { + bus: BUS, + dc: DC, + wr: WR, +} + +impl PGpio16BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + /// Create new parallel GPIO interface for communication with a display driver + pub fn new(bus: BUS, dc: DC, wr: WR) -> Self { + Self { bus, dc, wr } + } + + /// Consume the display interface and return + /// the bus and GPIO pins used by it + pub fn release(self) -> (BUS, DC, WR) { + (self.bus, self.dc, self.wr) + } + + fn send_word( + &mut self, + word: u16, + ) -> Result<(), ParallelError> { + self.wr.set_low().map_err(ParallelError::Wr)?; + self.bus.set_value(word).map_err(ParallelError::Bus)?; + self.wr.set_high().map_err(ParallelError::Wr) + } +} + +impl CommandInterface for PGpio16BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + type Error = ParallelError; + + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + self.dc.set_low().map_err(ParallelError::Dc)?; + self.send_word(u16::from(command))?; + self.dc.set_high().map_err(ParallelError::Dc)?; + + for arg in args { + self.send_word(u16::from(*arg))?; + } + + Ok(()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl PixelInterface for PGpio16BitInterface +where + BUS: OutputBus, + DC: OutputPin, + WR: OutputPin, +{ + fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { + self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes())) + } + + fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { + if count == 0 { + return Ok(()); + } + + self.send_pixel(pixel)?; + + for _ in 1..count { + self.wr.set_low().map_err(ParallelError::Wr)?; + self.wr.set_high().map_err(ParallelError::Wr)?; + } + Ok(()) + } +} diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs new file mode 100644 index 0000000..2892fa2 --- /dev/null +++ b/mipidsi/src/interface/spi.rs @@ -0,0 +1,156 @@ +use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; +use embedded_hal::{digital::OutputPin, spi::SpiDevice}; + +use super::{CommandInterface, PixelInterface}; + +/// Spi interface error +#[derive(Clone, Copy, Debug)] +pub enum SpiError { + /// SPI bus error + Spi(SPI), + /// Data/command pin error + Dc(DC), +} + +/// Spi interface +pub struct SpiInterface<'a, SPI, DC> { + spi: SPI, + dc: DC, + buffer: Buffer<'a>, +} + +impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { + /// Create new interface + pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { + let buffer = Buffer::new(buffer); + Self { spi, dc, buffer } + } +} + +impl<'a, SPI: SpiDevice, DC: OutputPin> CommandInterface for SpiInterface<'a, SPI, DC> { + type Error = SpiError; + + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + self.flush()?; + self.dc.set_low().map_err(SpiError::Dc)?; + self.spi.write(&[command]).map_err(SpiError::Spi)?; + self.dc.set_high().map_err(SpiError::Dc)?; + self.spi.write(args).map_err(SpiError::Spi)?; + Ok(()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.buffer + .flush(|buf| self.spi.write(buf)) + .map_err(SpiError::Spi) + } +} + +fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { + embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel) +} +fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { + embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel).map(|x| x << 2) +} + +impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface<'a, SPI, DC> { + fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { + self.buffer + .push_bytes(rgb565_to_bytes(pixel), |buf| self.spi.write(buf)) + .map_err(SpiError::Spi) + } + + fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { + self.buffer + .push_repeated_bytes(rgb565_to_bytes(pixel), count, |buf| self.spi.write(buf)) + .map_err(SpiError::Spi) + } +} + +impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface<'a, SPI, DC> { + fn send_pixel(&mut self, pixel: Rgb666) -> Result<(), Self::Error> { + self.buffer + .push_bytes(rgb666_to_bytes(pixel), |buf| self.spi.write(buf)) + .map_err(SpiError::Spi) + } + + fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { + self.buffer + .push_repeated_bytes(rgb666_to_bytes(pixel), count, |buf| self.spi.write(buf)) + .map_err(SpiError::Spi) + } +} + +struct Buffer<'a> { + bytes: &'a mut [u8], + index: usize, +} + +impl<'a> Buffer<'a> { + fn new(buffer: &'a mut [u8]) -> Self { + Self { + bytes: buffer, + index: 0, + } + } + + fn flush(&mut self, mut on_full: impl FnMut(&[u8]) -> Result<(), E>) -> Result<(), E> { + let index = core::mem::replace(&mut self.index, 0); + if index != 0 { + on_full(&self.bytes[0..index])?; + } + Ok(()) + } + + fn push_bytes( + &mut self, + pixel: [u8; N], + on_full: impl FnMut(&[u8]) -> Result<(), E>, + ) -> Result<(), E> { + if self.bytes.len() - self.index < N { + self.flush(on_full)?; + } + + self.bytes[self.index..][..N].copy_from_slice(&pixel); + self.index += N; + Ok(()) + } + + fn push_repeated_bytes( + &mut self, + pixel: [u8; N], + mut count: u32, + mut on_full: impl FnMut(&[u8]) -> Result<(), E>, + ) -> Result<(), E> { + if let Ok(count_bytes) = usize::try_from(count * N as u32) { + if count_bytes >= self.bytes.len() - self.index { + // There is enough remaining space in the buffer for all the new data + for i in 0..count as usize { + self.bytes[(self.index + (i * N))..][..N].copy_from_slice(&pixel); + } + self.index += count_bytes; + return Ok(()); + } + } + + self.flush(&mut on_full)?; + + let buffer_len = self.bytes.len() / N; + + let fill_count = core::cmp::min(count as usize, buffer_len); + + for i in 0..fill_count { + self.bytes[i * N..][..N].copy_from_slice(&pixel); + } + + while count >= buffer_len as u32 { + on_full(&self.bytes[0..(buffer_len * N)])?; + + count -= buffer_len as u32; + } + + self.index = count as usize * 2; + + Ok(()) + } +} diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 871983a..9c2dcbf 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -96,11 +96,11 @@ //! ## Troubleshooting //! See [document](https://github.com/almindor/mipidsi/blob/master/docs/TROUBLESHOOTING.md) -use dcs::{Dcs, WriteMemoryStart}; -use display_interface::{DataFormat, WriteOnlyDataCommand}; +use dcs::Dcs; + +pub mod interface; pub mod error; -use error::Error; use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; @@ -129,7 +129,7 @@ mod batch; /// pub struct Display where - DI: WriteOnlyDataCommand, + DI: interface::PixelInterface, MODEL: Model, RST: OutputPin, { @@ -149,7 +149,7 @@ where impl Display where - DI: WriteOnlyDataCommand, + DI: interface::PixelInterface, M: Model, RST: OutputPin, { @@ -171,7 +171,7 @@ where /// # let mut display = mipidsi::_mock::new_mock_display(); /// display.set_orientation(Orientation::default().rotate(Rotation::Deg180)).unwrap(); /// ``` - pub fn set_orientation(&mut self, orientation: options::Orientation) -> Result<(), Error> { + pub fn set_orientation(&mut self, orientation: options::Orientation) -> Result<(), DI::Error> { self.madctl = self.madctl.with_orientation(orientation); // set orientation self.dcs.write_command(self.madctl)?; @@ -195,12 +195,8 @@ where /// # let mut display = mipidsi::_mock::new_mock_display(); /// display.set_pixel(100, 200, Rgb565::new(251, 188, 20)).unwrap(); /// ``` - pub fn set_pixel(&mut self, x: u16, y: u16, color: M::ColorFormat) -> Result<(), Error> { - self.set_address_window(x, y, x, y)?; - self.model - .write_pixels(&mut self.dcs, core::iter::once(color))?; - - Ok(()) + pub fn set_pixel(&mut self, x: u16, y: u16, color: M::ColorFormat) -> Result<(), DI::Error> { + self.set_pixels(x, y, x, y, core::iter::once(color)) } /// @@ -237,77 +233,19 @@ where ex: u16, ey: u16, colors: T, - ) -> Result<(), Error> + ) -> Result<(), DI::Error> where T: IntoIterator, { self.set_address_window(sx, sy, ex, ey)?; - self.model.write_pixels(&mut self.dcs, colors)?; - Ok(()) - } + self.dcs.write_command(dcs::WriteMemoryStart)?; - /// Copies raw pixel data to a rectangular region of the framebuffer. - /// - /// This method writes the pixel data stored in the `raw_buf` slice to the - /// specified rectangular region within the display controller's - /// framebuffer. If `raw_buf` contains more data than required to fill the - /// region, writing will wrap around to the top left corner after reaching - /// the bottom right corner. - /// - ///
- /// - /// This method is intended for advanced use cases where low level access to - /// the displays framebuffer is required for performance reasons. The - /// caller must ensure the raw pixel data in `raw_buf` has the correct - /// format and endianness. - /// - /// For all other use cases it is recommended to instead use - /// [`embedded-graphics`](https://docs.rs/embedded-graphics/latest/embedded_graphics/) - /// to draw to the display. - /// - ///
- /// - ///
- /// - /// The method might not work the same for all `display-interface`s and is - /// known to not work as expected when used with a - /// [`PGPIO16BitInterface`](https://docs.rs/display-interface-parallel-gpio/latest/display_interface_parallel_gpio/struct.PGPIO16BitInterface.html) - /// from the `display-interface-gpio` crate. - /// - ///
- /// - /// # Arguments - /// - /// * `sx` - x coordinate start - /// * `sy` - y coordinate start - /// * `ex` - x coordinate end (inclusive) - /// * `ey` - y coordinate end (inclusive) - /// * `raw_buf` - `&[u8]` buffer of raw pixel data in the format expected by the display - /// - ///
- /// - /// The end values of the X and Y coordinate ranges are inclusive, and no - /// bounds checking is performed on these values. Using out of range values - /// (e.g., passing `320` instead of `319` for a 320 pixel wide display) will - /// result in undefined behavior. - /// - ///
- pub fn set_pixels_from_buffer( - &mut self, - sx: u16, - sy: u16, - ex: u16, - ey: u16, - raw_buf: &[u8], - ) -> Result<(), Error> { - self.set_address_window(sx, sy, ex, ey)?; - - self.dcs.write_command(WriteMemoryStart)?; - - self.dcs.di.send_data(DataFormat::U8(raw_buf))?; + for color in colors { + self.dcs.di.send_pixel(color)?; + } - Ok(()) + self.dcs.di.flush() } /// Sets the vertical scroll region. @@ -329,7 +267,7 @@ where &mut self, top_fixed_area: u16, bottom_fixed_area: u16, - ) -> Result<(), Error> { + ) -> Result<(), DI::Error> { let rows = M::FRAMEBUFFER_SIZE.1; let vscrdef = if top_fixed_area + bottom_fixed_area > rows { @@ -352,7 +290,7 @@ where /// /// Use [`set_vertical_scroll_region`](Self::set_vertical_scroll_region) to setup the scroll region, before /// using this method. - pub fn set_vertical_scroll_offset(&mut self, offset: u16) -> Result<(), Error> { + pub fn set_vertical_scroll_offset(&mut self, offset: u16) -> Result<(), DI::Error> { let vscad = dcs::SetScrollStart::new(offset); self.dcs.write_command(vscad) } @@ -366,7 +304,7 @@ where } // Sets the address window for the display. - fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), Error> { + fn set_address_window(&mut self, sx: u16, sy: u16, ex: u16, ey: u16) -> Result<(), DI::Error> { // add clipping offsets if present let mut offset = self.options.display_offset; let mapping = MemoryMapping::from(self.options.orientation); @@ -392,7 +330,7 @@ where pub fn set_tearing_effect( &mut self, tearing_effect: options::TearingEffect, - ) -> Result<(), Error> { + ) -> Result<(), DI::Error> { self.dcs .write_command(dcs::SetTearingEffect::new(tearing_effect)) } @@ -408,7 +346,7 @@ where /// Puts the display to sleep, reducing power consumption. /// Need to call [Self::wake] before issuing other commands /// - pub fn sleep(&mut self, delay: &mut D) -> Result<(), Error> { + pub fn sleep(&mut self, delay: &mut D) -> Result<(), DI::Error> { self.dcs.write_command(dcs::EnterSleepMode)?; // All supported models requires a 120ms delay before issuing other commands delay.delay_us(120_000); @@ -419,7 +357,7 @@ where /// /// Wakes the display after it's been set to sleep via [Self::sleep] /// - pub fn wake(&mut self, delay: &mut D) -> Result<(), Error> { + pub fn wake(&mut self, delay: &mut D) -> Result<(), DI::Error> { self.dcs.write_command(dcs::ExitSleepMode)?; // ST7789 and st7735s have the highest minimal delay of 120ms delay.delay_us(120_000); @@ -445,10 +383,15 @@ where /// Do not use types in this module outside of doc tests. #[doc(hidden)] pub mod _mock { - use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; + use core::convert::Infallible; + use embedded_hal::{delay::DelayNs, digital, spi}; - use crate::{models::ILI9341Rgb565, Builder, Display, NoResetPin}; + use crate::{ + interface::{CommandInterface, PixelInterface}, + models::ILI9341Rgb565, + Builder, Display, NoResetPin, + }; pub fn new_mock_display() -> Display { Builder::new(ILI9341Rgb565, MockDisplayInterface) @@ -495,12 +438,24 @@ pub mod _mock { pub struct MockDisplayInterface; - impl WriteOnlyDataCommand for MockDisplayInterface { - fn send_commands(&mut self, _cmd: DataFormat<'_>) -> Result<(), DisplayError> { + impl CommandInterface for MockDisplayInterface { + type Error = Infallible; + + fn send_command(&mut self, _command: u8, _args: &[u8]) -> Result<(), Self::Error> { + Ok(()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + } + + impl PixelInterface

for MockDisplayInterface { + fn send_pixel(&mut self, _pixel: P) -> Result<(), Self::Error> { Ok(()) } - fn send_data(&mut self, _buf: DataFormat<'_>) -> Result<(), DisplayError> { + fn send_repeated_pixel(&mut self, _pixel: P, _count: u32) -> Result<(), Self::Error> { Ok(()) } } diff --git a/mipidsi/src/models.rs b/mipidsi/src/models.rs index 16828bc..5144c9b 100644 --- a/mipidsi/src/models.rs +++ b/mipidsi/src/models.rs @@ -2,10 +2,9 @@ use crate::{ dcs::{Dcs, SetAddressMode}, - error::Error, + interface::CommandInterface, options::ModelOptions, }; -use display_interface::WriteOnlyDataCommand; use embedded_graphics_core::prelude::RgbColor; use embedded_hal::delay::DelayNs; @@ -42,16 +41,8 @@ pub trait Model { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand; - - /// Writes pixels to the display IC via the given display interface. - /// - /// Any pixel color format conversion is done here. - fn write_pixels(&mut self, di: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator; + DI: CommandInterface; } diff --git a/mipidsi/src/models/gc9a01.rs b/mipidsi/src/models/gc9a01.rs index 0936f25..680acbe 100644 --- a/mipidsi/src/models/gc9a01.rs +++ b/mipidsi/src/models/gc9a01.rs @@ -1,13 +1,12 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::{pixelcolor::Rgb565, prelude::IntoStorage}; +use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use crate::{ dcs::{ BitsPerPixel, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, - SetPixelFormat, WriteMemoryStart, + SetPixelFormat, }, - error::Error, + interface::CommandInterface, options::ModelOptions, }; @@ -25,10 +24,10 @@ impl Model for GC9A01 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); @@ -123,17 +122,4 @@ impl Model for GC9A01 { Ok(madctl) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().map(|c| c.into_storage()); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf)?; - Ok(()) - } } diff --git a/mipidsi/src/models/ili9341.rs b/mipidsi/src/models/ili9341.rs index d680dd4..7b2483a 100644 --- a/mipidsi/src/models/ili9341.rs +++ b/mipidsi/src/models/ili9341.rs @@ -1,10 +1,9 @@ -use display_interface::WriteOnlyDataCommand; use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::delay::DelayNs; use crate::{ dcs::{BitsPerPixel, Dcs, PixelFormat, SetAddressMode}, - error::Error, + interface::CommandInterface, models::{ili934x, Model}, options::ModelOptions, }; @@ -24,22 +23,14 @@ impl Model for ILI9341Rgb565 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - ili934x::write_pixels_rgb565(dcs, colors) - } } impl Model for ILI9341Rgb666 { @@ -51,20 +42,12 @@ impl Model for ILI9341Rgb666 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - ili934x::write_pixels_rgb666(dcs, colors) - } } diff --git a/mipidsi/src/models/ili9342c.rs b/mipidsi/src/models/ili9342c.rs index 41f59fa..e1fba04 100644 --- a/mipidsi/src/models/ili9342c.rs +++ b/mipidsi/src/models/ili9342c.rs @@ -1,10 +1,9 @@ -use display_interface::WriteOnlyDataCommand; use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::delay::DelayNs; use crate::{ dcs::{BitsPerPixel, Dcs, PixelFormat, SetAddressMode}, - error::Error, + interface::CommandInterface, models::{ili934x, Model}, options::ModelOptions, }; @@ -24,22 +23,14 @@ impl Model for ILI9342CRgb565 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - ili934x::write_pixels_rgb565(dcs, colors) - } } impl Model for ILI9342CRgb666 { @@ -51,20 +42,12 @@ impl Model for ILI9342CRgb666 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - ili934x::write_pixels_rgb666(dcs, colors) - } } diff --git a/mipidsi/src/models/ili934x.rs b/mipidsi/src/models/ili934x.rs index 7be75a5..f14275e 100644 --- a/mipidsi/src/models/ili934x.rs +++ b/mipidsi/src/models/ili934x.rs @@ -1,13 +1,11 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::pixelcolor::{IntoStorage, Rgb565, Rgb666, RgbColor}; use embedded_hal::delay::DelayNs; use crate::{ dcs::{ Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, - SetInvertMode, SetPixelFormat, WriteMemoryStart, + SetInvertMode, SetPixelFormat, }, - error::Error, + interface::CommandInterface, options::ModelOptions, }; @@ -17,10 +15,10 @@ pub fn init_common( delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, -) -> Result +) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); @@ -50,32 +48,3 @@ where Ok(madctl) } - -pub fn write_pixels_rgb565(dcs: &mut Dcs, colors: I) -> Result<(), Error> -where - DI: WriteOnlyDataCommand, - I: IntoIterator, -{ - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().map(|c| c.into_storage()); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf) -} - -pub fn write_pixels_rgb666(dcs: &mut Dcs, colors: I) -> Result<(), Error> -where - DI: WriteOnlyDataCommand, - I: IntoIterator, -{ - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().flat_map(|c| { - let red = c.r() << 2; - let green = c.g() << 2; - let blue = c.b() << 2; - [red, green, blue] - }); - - let buf = DataFormat::U8Iter(&mut iter); - dcs.di.send_data(buf) -} diff --git a/mipidsi/src/models/ili9486.rs b/mipidsi/src/models/ili9486.rs index 287a9a8..aa22e2f 100644 --- a/mipidsi/src/models/ili9486.rs +++ b/mipidsi/src/models/ili9486.rs @@ -1,16 +1,12 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::{ - pixelcolor::{Rgb565, Rgb666}, - prelude::{IntoStorage, RgbColor}, -}; +use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::delay::DelayNs; use crate::{ dcs::{ BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, - SetDisplayOn, SetInvertMode, SetPixelFormat, WriteMemoryStart, + SetDisplayOn, SetInvertMode, SetPixelFormat, }, - error::Error, + interface::CommandInterface, options::ModelOptions, }; @@ -31,28 +27,16 @@ impl Model for ILI9486Rgb565 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { delay.delay_us(120_000); let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); init_common(dcs, delay, options, pf) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().map(|c| c.into_storage()); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf) - } } impl Model for ILI9486Rgb666 { @@ -64,33 +48,16 @@ impl Model for ILI9486Rgb666 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { delay.delay_us(120_000); let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); init_common(dcs, delay, options, pf) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().flat_map(|c| { - let red = c.r() << 2; - let green = c.g() << 2; - let blue = c.b() << 2; - [red, green, blue] - }); - - let buf = DataFormat::U8Iter(&mut iter); - dcs.di.send_data(buf) - } } // common init for all color format models @@ -99,10 +66,10 @@ fn init_common( delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, -) -> Result +) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); dcs.write_command(ExitSleepMode)?; // turn off sleep diff --git a/mipidsi/src/models/st7735s.rs b/mipidsi/src/models/st7735s.rs index df2eba9..f6e741f 100644 --- a/mipidsi/src/models/st7735s.rs +++ b/mipidsi/src/models/st7735s.rs @@ -1,13 +1,12 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::{pixelcolor::Rgb565, prelude::IntoStorage}; +use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use crate::{ dcs::{ BitsPerPixel, Dcs, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, - SetPixelFormat, WriteMemoryStart, + SetPixelFormat, }, - error::Error, + interface::CommandInterface, models::Model, options::ModelOptions, }; @@ -24,10 +23,10 @@ impl Model for ST7735s { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); @@ -70,17 +69,4 @@ impl Model for ST7735s { Ok(madctl) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - let mut iter = colors.into_iter().map(|c| c.into_storage()); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf)?; - Ok(()) - } } diff --git a/mipidsi/src/models/st7789.rs b/mipidsi/src/models/st7789.rs index 14e1881..0f182f8 100644 --- a/mipidsi/src/models/st7789.rs +++ b/mipidsi/src/models/st7789.rs @@ -1,13 +1,12 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::{pixelcolor::Rgb565, prelude::IntoStorage}; +use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use crate::{ dcs::{ BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, - SetDisplayOn, SetInvertMode, SetPixelFormat, WriteMemoryStart, + SetDisplayOn, SetInvertMode, SetPixelFormat, }, - error::Error, + interface::CommandInterface, models::Model, options::ModelOptions, }; @@ -26,10 +25,10 @@ impl Model for ST7789 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); @@ -55,18 +54,4 @@ impl Model for ST7789 { Ok(madctl) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - - let mut iter = colors.into_iter().map(Rgb565::into_storage); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf)?; - Ok(()) - } } diff --git a/mipidsi/src/models/st7796.rs b/mipidsi/src/models/st7796.rs index 3ac7a06..271cd31 100644 --- a/mipidsi/src/models/st7796.rs +++ b/mipidsi/src/models/st7796.rs @@ -1,10 +1,9 @@ -use display_interface::WriteOnlyDataCommand; use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use crate::{ dcs::{Dcs, SetAddressMode}, - error::Error, + interface::CommandInterface, models::Model, options::ModelOptions, }; @@ -23,19 +22,11 @@ impl Model for ST7796 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { super::ST7789.init(dcs, delay, options) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - super::ST7789.write_pixels(dcs, colors) - } } diff --git a/mipidsi/tests/external.rs b/mipidsi/tests/external.rs index bf681de..1db8382 100644 --- a/mipidsi/tests/external.rs +++ b/mipidsi/tests/external.rs @@ -1,12 +1,11 @@ -use display_interface::{DataFormat, WriteOnlyDataCommand}; -use embedded_graphics_core::{pixelcolor::Rgb565, prelude::IntoStorage}; +use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use mipidsi::{ dcs::{ BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, - SetDisplayOn, SetInvertMode, SetPixelFormat, WriteMemoryStart, + SetDisplayOn, SetInvertMode, SetPixelFormat, }, - error::Error, + interface::CommandInterface, models::Model, options::ModelOptions, }; @@ -24,10 +23,10 @@ impl Model for ExternalST7789 { dcs: &mut Dcs, delay: &mut DELAY, options: &ModelOptions, - ) -> Result + ) -> Result where DELAY: DelayNs, - DI: WriteOnlyDataCommand, + DI: CommandInterface, { let madctl = SetAddressMode::from(options); @@ -53,18 +52,4 @@ impl Model for ExternalST7789 { Ok(madctl) } - - fn write_pixels(&mut self, dcs: &mut Dcs, colors: I) -> Result<(), Error> - where - DI: WriteOnlyDataCommand, - I: IntoIterator, - { - dcs.write_command(WriteMemoryStart)?; - - let mut iter = colors.into_iter().map(Rgb565::into_storage); - - let buf = DataFormat::U16BEIter(&mut iter); - dcs.di.send_data(buf)?; - Ok(()) - } } From b69e292b63962232225c32078ce6dabc58203dbc Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 16 Nov 2024 13:29:03 -0600 Subject: [PATCH 02/36] fixup! WIP --- mipidsi/src/interface/parallel.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 3064b8c..9c6a05b 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -235,8 +235,8 @@ where if byte1 == byte2 { self.send_byte(byte1)?; for _ in 1..(count * 2) { - self.dc.set_low().map_err(ParallelError::Dc)?; - self.dc.set_high().map_err(ParallelError::Dc)?; + self.wr.set_low().map_err(ParallelError::Wr)?; + self.wr.set_high().map_err(ParallelError::Wr)?; } Ok(()) } else { From 76fe47197296da7230713520fdc26032356ba3c2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 16 Nov 2024 18:10:49 -0600 Subject: [PATCH 03/36] Oops --- mipidsi/src/interface/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 2892fa2..d8491e5 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -123,7 +123,7 @@ impl<'a> Buffer<'a> { mut on_full: impl FnMut(&[u8]) -> Result<(), E>, ) -> Result<(), E> { if let Ok(count_bytes) = usize::try_from(count * N as u32) { - if count_bytes >= self.bytes.len() - self.index { + if count_bytes <= self.bytes.len() - self.index { // There is enough remaining space in the buffer for all the new data for i in 0..count as usize { self.bytes[(self.index + (i * N))..][..N].copy_from_slice(&pixel); From 9f054e3f258411c394c5ad661c3cb317175694ea Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 17 Nov 2024 09:21:30 -0600 Subject: [PATCH 04/36] Iterator --- mipidsi/src/interface/mod.rs | 7 +++++++ mipidsi/src/interface/spi.rs | 17 +++++++++++++++++ mipidsi/src/lib.rs | 5 ++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index ce00954..25e0967 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -23,6 +23,13 @@ pub trait PixelInterface: CommandInterface { /// [`CommandInterface::flush`] must be called to ensure the data is actually sent fn send_pixel(&mut self, pixel: P) -> Result<(), Self::Error>; + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + for pixel in pixels { + self.send_pixel(pixel)?; + } + Ok(()) + } + /// Send the same pixel value multiple times /// /// `WriteMemoryStart` must be sent before calling this function diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index d8491e5..0cb48a2 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -1,3 +1,4 @@ +use embedded_graphics_core::pixelcolor::raw::ToBytes; use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::{digital::OutputPin, spi::SpiDevice}; @@ -65,6 +66,22 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface< .push_repeated_bytes(rgb565_to_bytes(pixel), count, |buf| self.spi.write(buf)) .map_err(SpiError::Spi) } + + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + let mut pixels = pixels.into_iter(); + 'all_pixels: loop { + let remaining = &mut self.buffer.bytes[self.buffer.index..]; + for chunk in remaining.as_chunks_mut().0 { + let Some(pixel) = pixels.next() else { + break 'all_pixels; + }; + *chunk = pixel.to_be_bytes(); + self.buffer.index += 2; + } + self.flush()?; + } + Ok(()) + } } impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface<'a, SPI, DC> { diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 9c2dcbf..e4629c5 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![feature(slice_as_chunks)] // associated re-typing not supported in rust yet #![allow(clippy::type_complexity)] #![warn(missing_docs)] @@ -241,9 +242,7 @@ where self.dcs.write_command(dcs::WriteMemoryStart)?; - for color in colors { - self.dcs.di.send_pixel(color)?; - } + self.dcs.di.send_pixels(colors)?; self.dcs.di.flush() } From 503e255b9dec472032fc08c3bd7e15ac19d48d02 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 17 Nov 2024 11:14:32 -0600 Subject: [PATCH 05/36] Optimize spi --- mipidsi/src/interface/mod.rs | 6 + mipidsi/src/interface/spi.rs | 226 ++++++++++++++++++++++------------- 2 files changed, 152 insertions(+), 80 deletions(-) diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 25e0967..4750b25 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -1,4 +1,5 @@ //! Interface traits and implementations +#![allow(missing_docs)] /// Command interface pub trait CommandInterface { @@ -23,6 +24,11 @@ pub trait PixelInterface: CommandInterface { /// [`CommandInterface::flush`] must be called to ensure the data is actually sent fn send_pixel(&mut self, pixel: P) -> Result<(), Self::Error>; + /// Send a sequence of pixels + /// + /// `WriteMemoryStart` must be sent before calling this function + /// + /// [`CommandInterface::flush`] must be called to ensure the data is actually sent fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { for pixel in pixels { self.send_pixel(pixel)?; diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 0cb48a2..f91148c 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -1,4 +1,3 @@ -use embedded_graphics_core::pixelcolor::raw::ToBytes; use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::{digital::OutputPin, spi::SpiDevice}; @@ -14,36 +13,41 @@ pub enum SpiError { } /// Spi interface -pub struct SpiInterface<'a, SPI, DC> { +pub struct SpiInterface { spi: SPI, dc: DC, - buffer: Buffer<'a>, } -impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { +impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface, DC> { /// Create new interface pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { - let buffer = Buffer::new(buffer); - Self { spi, dc, buffer } + let spi = BufferedSpiAdapter::new(spi, buffer); + Self { spi, dc } } } -impl<'a, SPI: SpiDevice, DC: OutputPin> CommandInterface for SpiInterface<'a, SPI, DC> { +impl SpiInterface { + /// Create new interface + pub fn new_custom(spi: SPI, dc: DC) -> Self { + Self { spi, dc } + } +} + +impl CommandInterface for SpiInterface { type Error = SpiError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { self.flush()?; self.dc.set_low().map_err(SpiError::Dc)?; - self.spi.write(&[command]).map_err(SpiError::Spi)?; + self.spi.push_bytes(&[command]).map_err(SpiError::Spi)?; + self.spi.flush().map_err(SpiError::Spi)?; self.dc.set_high().map_err(SpiError::Dc)?; - self.spi.write(args).map_err(SpiError::Spi)?; + self.spi.push_bytes(args).map_err(SpiError::Spi)?; Ok(()) } fn flush(&mut self) -> Result<(), Self::Error> { - self.buffer - .flush(|buf| self.spi.write(buf)) - .map_err(SpiError::Spi) + self.spi.flush().map_err(SpiError::Spi) } } @@ -54,120 +58,182 @@ fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel).map(|x| x << 2) } -impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface<'a, SPI, DC> { +impl PixelInterface for SpiInterface { fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { - self.buffer - .push_bytes(rgb565_to_bytes(pixel), |buf| self.spi.write(buf)) + self.spi + .push_bytes(&rgb565_to_bytes(pixel)) .map_err(SpiError::Spi) } fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { - self.buffer - .push_repeated_bytes(rgb565_to_bytes(pixel), count, |buf| self.spi.write(buf)) + self.spi + .push_bytes_repeated(&rgb565_to_bytes(pixel), count) .map_err(SpiError::Spi) } fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - let mut pixels = pixels.into_iter(); - 'all_pixels: loop { - let remaining = &mut self.buffer.bytes[self.buffer.index..]; - for chunk in remaining.as_chunks_mut().0 { - let Some(pixel) = pixels.next() else { - break 'all_pixels; - }; - *chunk = pixel.to_be_bytes(); - self.buffer.index += 2; - } - self.flush()?; - } - Ok(()) + self.spi + .push_array_iter(pixels.into_iter().map(rgb565_to_bytes)) + .map_err(SpiError::Spi) } } -impl<'a, SPI: SpiDevice, DC: OutputPin> PixelInterface for SpiInterface<'a, SPI, DC> { +impl PixelInterface for SpiInterface { fn send_pixel(&mut self, pixel: Rgb666) -> Result<(), Self::Error> { - self.buffer - .push_bytes(rgb666_to_bytes(pixel), |buf| self.spi.write(buf)) + self.spi + .push_bytes(&rgb666_to_bytes(pixel)) .map_err(SpiError::Spi) } fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { - self.buffer - .push_repeated_bytes(rgb666_to_bytes(pixel), count, |buf| self.spi.write(buf)) + self.spi + .push_bytes_repeated(&rgb666_to_bytes(pixel), count) + .map_err(SpiError::Spi) + } + + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + self.spi + .push_array_iter(pixels.into_iter().map(rgb666_to_bytes)) .map_err(SpiError::Spi) } } -struct Buffer<'a> { - bytes: &'a mut [u8], +pub trait BufferedSpi { + type Error: core::fmt::Debug; + + fn fill_buffer(&mut self, filler: impl FnOnce(&mut [u8]) -> usize) -> Result<(), Self::Error>; + + fn flush(&mut self) -> Result<(), Self::Error>; + + fn push_bytes(&mut self, mut bytes: &[u8]) -> Result<(), Self::Error> { + while !bytes.is_empty() { + self.fill_buffer(|buffer| { + let len = core::cmp::min(buffer.len(), bytes.len()); + let (to_send, rest) = bytes.split_at(len); + bytes = rest; + buffer[0..len].copy_from_slice(to_send); + len + })?; + } + Ok(()) + } + + fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), Self::Error> { + for _ in 0..count { + self.push_bytes(bytes)?; + } + Ok(()) + } + + fn push_array_iter( + &mut self, + arrays: impl IntoIterator, + ) -> Result<(), Self::Error> { + let mut arrays = arrays.into_iter(); + + loop { + let mut i = 0; + self.fill_buffer(|buffer| { + for chunk in buffer.as_chunks_mut().0 { + let Some(array) = arrays.next() else { + break; + }; + *chunk = array; + i += 2; + } + i + })?; + if i == 0 { + break; + } + } + Ok(()) + } +} + +pub struct BufferedSpiAdapter<'a, SPI: SpiDevice> { + spi: SPI, + buffer: &'a mut [u8], index: usize, } -impl<'a> Buffer<'a> { - fn new(buffer: &'a mut [u8]) -> Self { +impl<'a, SPI: SpiDevice> BufferedSpiAdapter<'a, SPI> { + fn new(spi: SPI, buffer: &'a mut [u8]) -> Self { Self { - bytes: buffer, + spi, + buffer, index: 0, } } - fn flush(&mut self, mut on_full: impl FnMut(&[u8]) -> Result<(), E>) -> Result<(), E> { - let index = core::mem::replace(&mut self.index, 0); - if index != 0 { - on_full(&self.bytes[0..index])?; + // fn push_bytes(&mut self, pixel: [u8; N]) -> Result<(), SPI::Error> { + // if self.buffer.len() - self.index < N { + // self.flush()?; + // } + + // self.buffer[self.index..][..N].copy_from_slice(&pixel); + // self.index += N; + // Ok(()) + // } +} + +impl BufferedSpi for BufferedSpiAdapter<'_, SPI> { + type Error = SPI::Error; + + fn fill_buffer(&mut self, filler: impl FnOnce(&mut [u8]) -> usize) -> Result<(), Self::Error> { + if self.index == self.buffer.len() { + self.flush()?; } + let buffer = &mut self.buffer[self.index..]; + self.index += filler(buffer); + assert!(self.index <= self.buffer.len()); Ok(()) } - fn push_bytes( - &mut self, - pixel: [u8; N], - on_full: impl FnMut(&[u8]) -> Result<(), E>, - ) -> Result<(), E> { - if self.bytes.len() - self.index < N { - self.flush(on_full)?; + fn flush(&mut self) -> Result<(), Self::Error> { + let index = core::mem::replace(&mut self.index, 0); + if index != 0 { + self.spi.write(&self.buffer[0..index])?; } - - self.bytes[self.index..][..N].copy_from_slice(&pixel); - self.index += N; Ok(()) } - fn push_repeated_bytes( - &mut self, - pixel: [u8; N], - mut count: u32, - mut on_full: impl FnMut(&[u8]) -> Result<(), E>, - ) -> Result<(), E> { - if let Ok(count_bytes) = usize::try_from(count * N as u32) { - if count_bytes <= self.bytes.len() - self.index { - // There is enough remaining space in the buffer for all the new data - for i in 0..count as usize { - self.bytes[(self.index + (i * N))..][..N].copy_from_slice(&pixel); + fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), Self::Error> { + { + let this = &mut *self; + let mut count = count; + if let Ok(count_bytes) = usize::try_from(count * bytes.len() as u32) { + if count_bytes <= this.buffer.len() - this.index { + // There is enough remaining space in the buffer for all the new data + for i in 0..count as usize { + this.buffer[(this.index + (i * bytes.len()))..][..bytes.len()] + .copy_from_slice(bytes); + } + this.index += count_bytes; + return Ok(()); } - self.index += count_bytes; - return Ok(()); } - } - self.flush(&mut on_full)?; + this.flush()?; - let buffer_len = self.bytes.len() / N; + // let buffer_len = self.buffer.len() / bytes.len(); - let fill_count = core::cmp::min(count as usize, buffer_len); + let fill_count = core::cmp::min(count as usize, this.buffer.len() / bytes.len()); - for i in 0..fill_count { - self.bytes[i * N..][..N].copy_from_slice(&pixel); - } + for i in 0..fill_count { + this.buffer[i * bytes.len()..][..bytes.len()].copy_from_slice(bytes); + } - while count >= buffer_len as u32 { - on_full(&self.bytes[0..(buffer_len * N)])?; + while count >= fill_count as u32 { + this.index = fill_count * bytes.len(); + this.flush()?; - count -= buffer_len as u32; - } + count -= fill_count as u32; + } - self.index = count as usize * 2; + this.index = count as usize * bytes.len(); - Ok(()) + Ok(()) + } } } From 865ed956b5f8d74b1c6aabef1977b2a2ac62a5ae Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Thu, 21 Nov 2024 20:55:38 -0600 Subject: [PATCH 06/36] Remove single pixel method --- mipidsi/src/interface/mod.rs | 14 +------------- mipidsi/src/interface/parallel.rs | 22 ++++++++++++---------- mipidsi/src/interface/spi.rs | 12 ------------ mipidsi/src/lib.rs | 2 +- 4 files changed, 14 insertions(+), 36 deletions(-) diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 4750b25..f610f2f 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -17,24 +17,12 @@ pub trait CommandInterface { /// Pixel interface pub trait PixelInterface: CommandInterface { - /// Send a single pixel - /// - /// `WriteMemoryStart` must be sent before calling this function - /// - /// [`CommandInterface::flush`] must be called to ensure the data is actually sent - fn send_pixel(&mut self, pixel: P) -> Result<(), Self::Error>; - /// Send a sequence of pixels /// /// `WriteMemoryStart` must be sent before calling this function /// /// [`CommandInterface::flush`] must be called to ensure the data is actually sent - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - for pixel in pixels { - self.send_pixel(pixel)?; - } - Ok(()) - } + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error>; /// Send the same pixel value multiple times /// diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 9c6a05b..1826d4e 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -220,9 +220,11 @@ where DC: OutputPin, WR: OutputPin, { - fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { - for byte in pixel.to_be_bytes() { - self.send_byte(byte)?; + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + for pixel in pixels { + for byte in pixel.to_be_bytes() { + self.send_byte(byte)?; + } } Ok(()) } @@ -240,10 +242,7 @@ where } Ok(()) } else { - for _ in 0..count { - self.send_pixel(pixel)?; - } - Ok(()) + self.send_pixels((0..count).map(|_| pixel)) } } } @@ -321,8 +320,11 @@ where DC: OutputPin, WR: OutputPin, { - fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { - self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes())) + fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + for pixel in pixels { + self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes()))?; + } + Ok(()) } fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { @@ -330,7 +332,7 @@ where return Ok(()); } - self.send_pixel(pixel)?; + self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes()))?; for _ in 1..count { self.wr.set_low().map_err(ParallelError::Wr)?; diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index f91148c..ae1a8f4 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -59,12 +59,6 @@ fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { } impl PixelInterface for SpiInterface { - fn send_pixel(&mut self, pixel: Rgb565) -> Result<(), Self::Error> { - self.spi - .push_bytes(&rgb565_to_bytes(pixel)) - .map_err(SpiError::Spi) - } - fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { self.spi .push_bytes_repeated(&rgb565_to_bytes(pixel), count) @@ -79,12 +73,6 @@ impl PixelInterface for SpiInterface PixelInterface for SpiInterface { - fn send_pixel(&mut self, pixel: Rgb666) -> Result<(), Self::Error> { - self.spi - .push_bytes(&rgb666_to_bytes(pixel)) - .map_err(SpiError::Spi) - } - fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { self.spi .push_bytes_repeated(&rgb666_to_bytes(pixel), count) diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index e4629c5..b2bbfba 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -450,7 +450,7 @@ pub mod _mock { } impl PixelInterface

for MockDisplayInterface { - fn send_pixel(&mut self, _pixel: P) -> Result<(), Self::Error> { + fn send_pixels(&mut self, _pixels: impl IntoIterator) -> Result<(), Self::Error> { Ok(()) } From ad84483f7d60c8f52b0c0a19300e8511bda76b60 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Thu, 21 Nov 2024 21:26:04 -0600 Subject: [PATCH 07/36] Remove slice_as_chunks --- mipidsi/src/interface/spi.rs | 6 +++--- mipidsi/src/lib.rs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index ae1a8f4..3bf8942 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -122,12 +122,12 @@ pub trait BufferedSpi { loop { let mut i = 0; self.fill_buffer(|buffer| { - for chunk in buffer.as_chunks_mut().0 { + for chunk in buffer.chunks_exact_mut(N) { let Some(array) = arrays.next() else { break; }; - *chunk = array; - i += 2; + chunk.copy_from_slice(&array); + i += N; } i })?; diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index b2bbfba..c451468 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(slice_as_chunks)] // associated re-typing not supported in rust yet #![allow(clippy::type_complexity)] #![warn(missing_docs)] From 2128d794dfe423b5c47e1afc26416a6d8b5daf38 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 22 Nov 2024 14:56:21 -0600 Subject: [PATCH 08/36] WIP --- mipidsi/src/interface/spi.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 3bf8942..66d6b14 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -119,21 +119,22 @@ pub trait BufferedSpi { ) -> Result<(), Self::Error> { let mut arrays = arrays.into_iter(); - loop { - let mut i = 0; + let mut done = false; + while !done { self.fill_buffer(|buffer| { + let mut i = 0; + // TODO: make sure buffer will hold at least one chunk for chunk in buffer.chunks_exact_mut(N) { - let Some(array) = arrays.next() else { + if let Some(array) = arrays.next() { + chunk.copy_from_slice(&array); + i += N; + } else { + done = true; break; }; - chunk.copy_from_slice(&array); - i += N; } i })?; - if i == 0 { - break; - } } Ok(()) } From 394a7f43b555ab46bd5769e1af821cffa94e1af3 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 22 Nov 2024 15:22:08 -0600 Subject: [PATCH 09/36] WIP --- mipidsi/src/interface/mod.rs | 1 - mipidsi/src/interface/spi.rs | 134 +++++++++++++---------------------- 2 files changed, 49 insertions(+), 86 deletions(-) diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index f610f2f..3194655 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -1,5 +1,4 @@ //! Interface traits and implementations -#![allow(missing_docs)] /// Command interface pub trait CommandInterface { diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 66d6b14..a543dc0 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -13,12 +13,12 @@ pub enum SpiError { } /// Spi interface -pub struct SpiInterface { - spi: SPI, +pub struct SpiInterface<'a, SPI, DC> { + spi: BufferedSpiAdapter<'a, SPI>, dc: DC, } -impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface, DC> { +impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { /// Create new interface pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { let spi = BufferedSpiAdapter::new(spi, buffer); @@ -26,14 +26,7 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface } } -impl SpiInterface { - /// Create new interface - pub fn new_custom(spi: SPI, dc: DC) -> Self { - Self { spi, dc } - } -} - -impl CommandInterface for SpiInterface { +impl CommandInterface for SpiInterface<'_, SPI, DC> { type Error = SpiError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -58,7 +51,7 @@ fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel).map(|x| x << 2) } -impl PixelInterface for SpiInterface { +impl PixelInterface for SpiInterface<'_, SPI, DC> { fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { self.spi .push_bytes_repeated(&rgb565_to_bytes(pixel), count) @@ -72,7 +65,7 @@ impl PixelInterface for SpiInterface PixelInterface for SpiInterface { +impl PixelInterface for SpiInterface<'_, SPI, DC> { fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { self.spi .push_bytes_repeated(&rgb666_to_bytes(pixel), count) @@ -86,61 +79,7 @@ impl PixelInterface for SpiInterface usize) -> Result<(), Self::Error>; - - fn flush(&mut self) -> Result<(), Self::Error>; - - fn push_bytes(&mut self, mut bytes: &[u8]) -> Result<(), Self::Error> { - while !bytes.is_empty() { - self.fill_buffer(|buffer| { - let len = core::cmp::min(buffer.len(), bytes.len()); - let (to_send, rest) = bytes.split_at(len); - bytes = rest; - buffer[0..len].copy_from_slice(to_send); - len - })?; - } - Ok(()) - } - - fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), Self::Error> { - for _ in 0..count { - self.push_bytes(bytes)?; - } - Ok(()) - } - - fn push_array_iter( - &mut self, - arrays: impl IntoIterator, - ) -> Result<(), Self::Error> { - let mut arrays = arrays.into_iter(); - - let mut done = false; - while !done { - self.fill_buffer(|buffer| { - let mut i = 0; - // TODO: make sure buffer will hold at least one chunk - for chunk in buffer.chunks_exact_mut(N) { - if let Some(array) = arrays.next() { - chunk.copy_from_slice(&array); - i += N; - } else { - done = true; - break; - }; - } - i - })?; - } - Ok(()) - } -} - -pub struct BufferedSpiAdapter<'a, SPI: SpiDevice> { +struct BufferedSpiAdapter<'a, SPI> { spi: SPI, buffer: &'a mut [u8], index: usize, @@ -155,21 +94,7 @@ impl<'a, SPI: SpiDevice> BufferedSpiAdapter<'a, SPI> { } } - // fn push_bytes(&mut self, pixel: [u8; N]) -> Result<(), SPI::Error> { - // if self.buffer.len() - self.index < N { - // self.flush()?; - // } - - // self.buffer[self.index..][..N].copy_from_slice(&pixel); - // self.index += N; - // Ok(()) - // } -} - -impl BufferedSpi for BufferedSpiAdapter<'_, SPI> { - type Error = SPI::Error; - - fn fill_buffer(&mut self, filler: impl FnOnce(&mut [u8]) -> usize) -> Result<(), Self::Error> { + fn fill_buffer(&mut self, filler: impl FnOnce(&mut [u8]) -> usize) -> Result<(), SPI::Error> { if self.index == self.buffer.len() { self.flush()?; } @@ -179,7 +104,7 @@ impl BufferedSpi for BufferedSpiAdapter<'_, SPI> { Ok(()) } - fn flush(&mut self) -> Result<(), Self::Error> { + fn flush(&mut self) -> Result<(), SPI::Error> { let index = core::mem::replace(&mut self.index, 0); if index != 0 { self.spi.write(&self.buffer[0..index])?; @@ -187,7 +112,7 @@ impl BufferedSpi for BufferedSpiAdapter<'_, SPI> { Ok(()) } - fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), Self::Error> { + fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), SPI::Error> { { let this = &mut *self; let mut count = count; @@ -225,4 +150,43 @@ impl BufferedSpi for BufferedSpiAdapter<'_, SPI> { Ok(()) } } + + fn push_bytes(&mut self, mut bytes: &[u8]) -> Result<(), SPI::Error> { + while !bytes.is_empty() { + self.fill_buffer(|buffer| { + let len = core::cmp::min(buffer.len(), bytes.len()); + let (to_send, remainder) = bytes.split_at(len); + bytes = remainder; + buffer[0..len].copy_from_slice(to_send); + len + })?; + } + Ok(()) + } + + fn push_array_iter( + &mut self, + arrays: impl IntoIterator, + ) -> Result<(), SPI::Error> { + let mut arrays = arrays.into_iter(); + + let mut done = false; + while !done { + self.fill_buffer(|buffer| { + let mut i = 0; + // TODO: make sure buffer will hold at least one chunk + for chunk in buffer.chunks_exact_mut(N) { + if let Some(array) = arrays.next() { + chunk.copy_from_slice(&array); + i += N; + } else { + done = true; + break; + }; + } + i + })?; + } + Ok(()) + } } From b8a73789fa58d9e33fa5c5e6d8f9118485e01535 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 07:17:49 -0600 Subject: [PATCH 10/36] Simplify spi --- mipidsi/src/interface/spi.rs | 188 +++++++++++------------------------ 1 file changed, 60 insertions(+), 128 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index a543dc0..e6396f2 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -14,15 +14,65 @@ pub enum SpiError { /// Spi interface pub struct SpiInterface<'a, SPI, DC> { - spi: BufferedSpiAdapter<'a, SPI>, + spi: SPI, dc: DC, + buffer: &'a mut [u8], } impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { /// Create new interface pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { - let spi = BufferedSpiAdapter::new(spi, buffer); - Self { spi, dc } + Self { spi, dc, buffer } + } + + fn push_bytes_repeated( + &mut self, + bytes: [u8; N], + count: u32, + ) -> Result<(), SPI::Error> { + let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); + let filled_len = fill_count as usize * N; + for chunk in self.buffer[..(filled_len)].chunks_exact_mut(N) { + let chunk: &mut [u8; N] = chunk.try_into().unwrap(); + *chunk = bytes; + } + + let mut count = count; + while count >= fill_count { + self.spi.write(&self.buffer[..filled_len])?; + count -= fill_count; + } + if count != 0 { + self.spi + .write(&self.buffer[..(count as usize * bytes.len())])?; + } + Ok(()) + } + + fn push_array_iter( + &mut self, + arrays: impl IntoIterator, + ) -> Result<(), SPI::Error> { + let mut arrays = arrays.into_iter(); + + assert!(self.buffer.len() >= N); + + let mut done = false; + while !done { + let mut i = 0; + for chunk in self.buffer.chunks_exact_mut(N) { + if let Some(array) = arrays.next() { + let chunk: &mut [u8; N] = chunk.try_into().unwrap(); + *chunk = array; + i += N; + } else { + done = true; + break; + }; + } + self.spi.write(&self.buffer[..i])?; + } + Ok(()) } } @@ -30,17 +80,15 @@ impl CommandInterface for SpiInterface<'_, SPI, D type Error = SpiError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { - self.flush()?; self.dc.set_low().map_err(SpiError::Dc)?; - self.spi.push_bytes(&[command]).map_err(SpiError::Spi)?; - self.spi.flush().map_err(SpiError::Spi)?; + self.spi.write(&[command]).map_err(SpiError::Spi)?; self.dc.set_high().map_err(SpiError::Dc)?; - self.spi.push_bytes(args).map_err(SpiError::Spi)?; + self.spi.write(args).map_err(SpiError::Spi)?; Ok(()) } fn flush(&mut self) -> Result<(), Self::Error> { - self.spi.flush().map_err(SpiError::Spi) + Ok(()) } } @@ -53,140 +101,24 @@ fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { impl PixelInterface for SpiInterface<'_, SPI, DC> { fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { - self.spi - .push_bytes_repeated(&rgb565_to_bytes(pixel), count) + self.push_bytes_repeated(rgb565_to_bytes(pixel), count) .map_err(SpiError::Spi) } fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - self.spi - .push_array_iter(pixels.into_iter().map(rgb565_to_bytes)) + self.push_array_iter(pixels.into_iter().map(rgb565_to_bytes)) .map_err(SpiError::Spi) } } impl PixelInterface for SpiInterface<'_, SPI, DC> { fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { - self.spi - .push_bytes_repeated(&rgb666_to_bytes(pixel), count) + self.push_bytes_repeated(rgb666_to_bytes(pixel), count) .map_err(SpiError::Spi) } fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - self.spi - .push_array_iter(pixels.into_iter().map(rgb666_to_bytes)) + self.push_array_iter(pixels.into_iter().map(rgb666_to_bytes)) .map_err(SpiError::Spi) } } - -struct BufferedSpiAdapter<'a, SPI> { - spi: SPI, - buffer: &'a mut [u8], - index: usize, -} - -impl<'a, SPI: SpiDevice> BufferedSpiAdapter<'a, SPI> { - fn new(spi: SPI, buffer: &'a mut [u8]) -> Self { - Self { - spi, - buffer, - index: 0, - } - } - - fn fill_buffer(&mut self, filler: impl FnOnce(&mut [u8]) -> usize) -> Result<(), SPI::Error> { - if self.index == self.buffer.len() { - self.flush()?; - } - let buffer = &mut self.buffer[self.index..]; - self.index += filler(buffer); - assert!(self.index <= self.buffer.len()); - Ok(()) - } - - fn flush(&mut self) -> Result<(), SPI::Error> { - let index = core::mem::replace(&mut self.index, 0); - if index != 0 { - self.spi.write(&self.buffer[0..index])?; - } - Ok(()) - } - - fn push_bytes_repeated(&mut self, bytes: &[u8], count: u32) -> Result<(), SPI::Error> { - { - let this = &mut *self; - let mut count = count; - if let Ok(count_bytes) = usize::try_from(count * bytes.len() as u32) { - if count_bytes <= this.buffer.len() - this.index { - // There is enough remaining space in the buffer for all the new data - for i in 0..count as usize { - this.buffer[(this.index + (i * bytes.len()))..][..bytes.len()] - .copy_from_slice(bytes); - } - this.index += count_bytes; - return Ok(()); - } - } - - this.flush()?; - - // let buffer_len = self.buffer.len() / bytes.len(); - - let fill_count = core::cmp::min(count as usize, this.buffer.len() / bytes.len()); - - for i in 0..fill_count { - this.buffer[i * bytes.len()..][..bytes.len()].copy_from_slice(bytes); - } - - while count >= fill_count as u32 { - this.index = fill_count * bytes.len(); - this.flush()?; - - count -= fill_count as u32; - } - - this.index = count as usize * bytes.len(); - - Ok(()) - } - } - - fn push_bytes(&mut self, mut bytes: &[u8]) -> Result<(), SPI::Error> { - while !bytes.is_empty() { - self.fill_buffer(|buffer| { - let len = core::cmp::min(buffer.len(), bytes.len()); - let (to_send, remainder) = bytes.split_at(len); - bytes = remainder; - buffer[0..len].copy_from_slice(to_send); - len - })?; - } - Ok(()) - } - - fn push_array_iter( - &mut self, - arrays: impl IntoIterator, - ) -> Result<(), SPI::Error> { - let mut arrays = arrays.into_iter(); - - let mut done = false; - while !done { - self.fill_buffer(|buffer| { - let mut i = 0; - // TODO: make sure buffer will hold at least one chunk - for chunk in buffer.chunks_exact_mut(N) { - if let Some(array) = arrays.next() { - chunk.copy_from_slice(&array); - i += N; - } else { - done = true; - break; - }; - } - i - })?; - } - Ok(()) - } -} From 4bd8ab68a4f076b88621210bd1236981438a116f Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 09:39:13 -0600 Subject: [PATCH 11/36] WIP --- mipidsi/src/batch.rs | 12 +++- mipidsi/src/builder.rs | 11 +-- mipidsi/src/graphics.rs | 10 +-- mipidsi/src/interface/mod.rs | 112 ++++++++++++++++++++++++++++-- mipidsi/src/interface/parallel.rs | 68 +++++++++++++----- mipidsi/src/interface/spi.rs | 38 ++++------ mipidsi/src/lib.rs | 24 +++++-- 7 files changed, 208 insertions(+), 67 deletions(-) diff --git a/mipidsi/src/batch.rs b/mipidsi/src/batch.rs index a04f8e9..d3c71cc 100644 --- a/mipidsi/src/batch.rs +++ b/mipidsi/src/batch.rs @@ -1,14 +1,19 @@ //! Original code from: [this repo](https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs) //! Batch the pixels to be rendered into Pixel Rows and Pixel Blocks (contiguous Pixel Rows). //! This enables the pixels to be rendered efficiently as Pixel Blocks, which may be transmitted in a single Non-Blocking SPI request. -use crate::{interface::PixelInterface, models::Model, Display}; +use crate::{ + interface::{PixelFormat, PixelInterface}, + models::Model, + Display, +}; use embedded_graphics_core::prelude::*; use embedded_hal::digital::OutputPin; pub trait DrawBatch where - DI: PixelInterface, + DI: PixelInterface, M: Model, + M::ColorFormat: PixelFormat, I: IntoIterator>, { fn draw_batch(&mut self, item_pixels: I) -> Result<(), DI::Error>; @@ -16,8 +21,9 @@ where impl DrawBatch for Display where - DI: PixelInterface, + DI: PixelInterface, M: Model, + M::ColorFormat: PixelFormat, I: IntoIterator>, RST: OutputPin, { diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index f62e09f..c31ae84 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -3,7 +3,7 @@ use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; -use crate::interface::PixelInterface; +use crate::interface::{PixelFormat, PixelInterface}; use crate::{dcs::Dcs, error::InitError, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -28,8 +28,9 @@ use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, Refr /// ``` pub struct Builder where - DI: PixelInterface, + DI: PixelInterface, MODEL: Model, + MODEL::ColorFormat: PixelFormat, { di: DI, model: MODEL, @@ -39,8 +40,9 @@ where impl Builder where - DI: PixelInterface, + DI: PixelInterface, MODEL: Model, + MODEL::ColorFormat: PixelFormat, { /// /// Constructs a new builder for given [Model]. @@ -58,8 +60,9 @@ where impl Builder where - DI: PixelInterface, + DI: PixelInterface, MODEL: Model, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { /// diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 2b3d81a..c0d2f45 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -7,14 +7,15 @@ use embedded_graphics_core::{ }; use embedded_hal::digital::OutputPin; -use crate::Display; use crate::{dcs::BitsPerPixel, interface::PixelInterface}; use crate::{dcs::WriteMemoryStart, models::Model}; +use crate::{interface::PixelFormat, Display}; impl DrawTarget for Display where - DI: PixelInterface, + DI: PixelInterface, M: Model, + M::ColorFormat: PixelFormat, RST: OutputPin, { type Error = DI::Error; @@ -111,15 +112,16 @@ where self.set_address_window(sx, sy, ex, ey)?; self.dcs.write_command(WriteMemoryStart)?; - self.dcs.di.send_repeated_pixel(color, count)?; + M::ColorFormat::send_repeated_pixel(&mut self.dcs.di, color, count)?; self.dcs.di.flush() } } impl OriginDimensions for Display where - DI: PixelInterface, + DI: PixelInterface, MODEL: Model, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { fn size(&self) -> Size { diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 3194655..e93b983 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -1,5 +1,12 @@ //! Interface traits and implementations +mod spi; +use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; +pub use spi::*; + +mod parallel; +pub use parallel::*; + /// Command interface pub trait CommandInterface { /// Error type @@ -15,24 +22,115 @@ pub trait CommandInterface { } /// Pixel interface -pub trait PixelInterface: CommandInterface { +pub trait PixelInterface: CommandInterface { + /// The native width of the interface + /// + /// In most cases this will be u8, except for larger parallel interfaces such as + /// 16 bit (currently supported) + /// or 9 or 18 bit (currently unsupported) + type PixelWord: Copy; /// Send a sequence of pixels /// /// `WriteMemoryStart` must be sent before calling this function /// /// [`CommandInterface::flush`] must be called to ensure the data is actually sent - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error>; + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error>; /// Send the same pixel value multiple times /// /// `WriteMemoryStart` must be sent before calling this function /// /// [`CommandInterface::flush`] must be called to ensure the data is actually sent - fn send_repeated_pixel(&mut self, pixel: P, count: u32) -> Result<(), Self::Error>; + fn send_repeated_pixel( + &mut self, + pixel: [Self::PixelWord; N], + count: u32, + ) -> Result<(), Self::Error>; } -mod spi; -pub use spi::*; +fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { + embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel) +} +fn rgb565_to_u16(pixel: Rgb565) -> [u16; 1] { + [u16::from_ne_bytes( + embedded_graphics_core::pixelcolor::raw::ToBytes::to_ne_bytes(pixel), + )] +} +fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { + embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel).map(|x| x << 2) +} -mod parallel; -pub use parallel::*; +/// This is an implementation detail, it should not be implemented or used outside this crate +pub trait PixelFormat { + // this should just be + // const N: usize; + // fn convert(self) -> [Word; Self::N]; + // but that doesn't work yet + + #[doc(hidden)] + fn send_pixels>( + dcs: &mut DCS, + pixels: impl IntoIterator, + ) -> Result<(), DCS::Error>; + + #[doc(hidden)] + fn send_repeated_pixel>( + dcs: &mut DCS, + pixel: Self, + count: u32, + ) -> Result<(), DCS::Error>; +} + +impl PixelFormat for Rgb565 { + fn send_pixels>( + dcs: &mut DCS, + pixels: impl IntoIterator, + ) -> Result<(), DCS::Error> { + dcs.send_pixels(pixels.into_iter().map(rgb565_to_bytes)) + } + + fn send_repeated_pixel>( + dcs: &mut DCS, + pixel: Self, + count: u32, + ) -> Result<(), DCS::Error> { + dcs.send_repeated_pixel(rgb565_to_bytes(pixel), count) + } +} + +impl PixelFormat for Rgb666 { + fn send_pixels>( + dcs: &mut DCS, + pixels: impl IntoIterator, + ) -> Result<(), DCS::Error> { + dcs.send_pixels(pixels.into_iter().map(rgb666_to_bytes)) + } + + fn send_repeated_pixel>( + dcs: &mut DCS, + pixel: Self, + count: u32, + ) -> Result<(), DCS::Error> { + dcs.send_repeated_pixel(rgb666_to_bytes(pixel), count) + } +} + +impl PixelFormat for Rgb565 { + fn send_pixels>( + dcs: &mut DCS, + pixels: impl IntoIterator, + ) -> Result<(), DCS::Error> { + dcs.send_pixels(pixels.into_iter().map(rgb565_to_u16)) + } + + fn send_repeated_pixel>( + dcs: &mut DCS, + pixel: Self, + count: u32, + ) -> Result<(), DCS::Error> { + dcs.send_repeated_pixel(rgb565_to_u16(pixel), count) + } +} diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 1826d4e..93632a6 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -1,4 +1,3 @@ -use embedded_graphics_core::pixelcolor::{raw::ToBytes, Rgb565}; use embedded_hal::digital::OutputPin; use super::{CommandInterface, PixelInterface}; @@ -214,29 +213,38 @@ where } } -impl PixelInterface for PGpio8BitInterface +impl PixelInterface for PGpio8BitInterface where BUS: OutputBus, DC: OutputPin, WR: OutputPin, { - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + type PixelWord = u8; + + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { for pixel in pixels { - for byte in pixel.to_be_bytes() { + for byte in pixel { self.send_byte(byte)?; } } Ok(()) } - fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { + fn send_repeated_pixel( + &mut self, + pixel: [Self::PixelWord; N], + count: u32, + ) -> Result<(), Self::Error> { if count == 0 { return Ok(()); } - let [byte1, byte2] = pixel.to_be_bytes(); - if byte1 == byte2 { - self.send_byte(byte1)?; - for _ in 1..(count * 2) { + + if let Some(word) = is_same(pixel) { + self.send_byte(word)?; + for _ in 1..(count * N as u32) { self.wr.set_low().map_err(ParallelError::Wr)?; self.wr.set_high().map_err(ParallelError::Wr)?; } @@ -314,30 +322,54 @@ where } } -impl PixelInterface for PGpio16BitInterface +impl PixelInterface for PGpio16BitInterface where BUS: OutputBus, DC: OutputPin, WR: OutputPin, { - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { + type PixelWord = u16; + + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { for pixel in pixels { - self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes()))?; + for word in pixel { + self.send_word(word)?; + } } Ok(()) } - fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { + fn send_repeated_pixel( + &mut self, + pixel: [Self::PixelWord; N], + count: u32, + ) -> Result<(), Self::Error> { if count == 0 { return Ok(()); } - self.send_word(u16::from_ne_bytes(pixel.to_ne_bytes()))?; + if let Some(word) = is_same(pixel) { + self.send_word(word)?; + for _ in 1..(count * N as u32) { + self.wr.set_low().map_err(ParallelError::Wr)?; + self.wr.set_high().map_err(ParallelError::Wr)?; + } + Ok(()) + } else { + self.send_pixels((0..count).map(|_| pixel)) + } + } +} - for _ in 1..count { - self.wr.set_low().map_err(ParallelError::Wr)?; - self.wr.set_high().map_err(ParallelError::Wr)?; +fn is_same(array: [T; N]) -> Option { + let (&first, rest) = array.split_first()?; + for &x in rest { + if x != first { + return None; } - Ok(()) } + Some(first) } diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index e6396f2..821d338 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -1,4 +1,3 @@ -use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::{digital::OutputPin, spi::SpiDevice}; use super::{CommandInterface, PixelInterface}; @@ -92,33 +91,22 @@ impl CommandInterface for SpiInterface<'_, SPI, D } } -fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { - embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel) -} -fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { - embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel).map(|x| x << 2) -} +impl PixelInterface for SpiInterface<'_, SPI, DC> { + type PixelWord = u8; -impl PixelInterface for SpiInterface<'_, SPI, DC> { - fn send_repeated_pixel(&mut self, pixel: Rgb565, count: u32) -> Result<(), Self::Error> { - self.push_bytes_repeated(rgb565_to_bytes(pixel), count) - .map_err(SpiError::Spi) - } - - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - self.push_array_iter(pixels.into_iter().map(rgb565_to_bytes)) - .map_err(SpiError::Spi) - } -} - -impl PixelInterface for SpiInterface<'_, SPI, DC> { - fn send_repeated_pixel(&mut self, pixel: Rgb666, count: u32) -> Result<(), Self::Error> { - self.push_bytes_repeated(rgb666_to_bytes(pixel), count) + fn send_repeated_pixel( + &mut self, + pixel: [Self::PixelWord; N], + count: u32, + ) -> Result<(), Self::Error> { + self.push_bytes_repeated(pixel, count) .map_err(SpiError::Spi) } - fn send_pixels(&mut self, pixels: impl IntoIterator) -> Result<(), Self::Error> { - self.push_array_iter(pixels.into_iter().map(rgb666_to_bytes)) - .map_err(SpiError::Spi) + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { + self.push_array_iter(pixels).map_err(SpiError::Spi) } } diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index c451468..0aa2121 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -106,6 +106,7 @@ use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; pub mod options; +use interface::PixelFormat; use options::MemoryMapping; mod builder; @@ -129,8 +130,9 @@ mod batch; /// pub struct Display where - DI: interface::PixelInterface, + DI: interface::PixelInterface, MODEL: Model, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { // DCS provider @@ -149,8 +151,9 @@ where impl Display where - DI: interface::PixelInterface, + DI: interface::PixelInterface, M: Model, + M::ColorFormat: PixelFormat, RST: OutputPin, { /// @@ -241,7 +244,7 @@ where self.dcs.write_command(dcs::WriteMemoryStart)?; - self.dcs.di.send_pixels(colors)?; + M::ColorFormat::send_pixels(&mut self.dcs.di, colors.into_iter())?; self.dcs.di.flush() } @@ -448,12 +451,21 @@ pub mod _mock { } } - impl PixelInterface

for MockDisplayInterface { - fn send_pixels(&mut self, _pixels: impl IntoIterator) -> Result<(), Self::Error> { + impl PixelInterface for MockDisplayInterface { + type PixelWord = u8; + + fn send_pixels( + &mut self, + _pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { Ok(()) } - fn send_repeated_pixel(&mut self, _pixel: P, _count: u32) -> Result<(), Self::Error> { + fn send_repeated_pixel( + &mut self, + _pixel: [Self::PixelWord; N], + _count: u32, + ) -> Result<(), Self::Error> { Ok(()) } } From c8eadb6386c06a725bc7093a6c138a1c8bb2d3e0 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:03:49 -0600 Subject: [PATCH 12/36] Replace Dcs with InterfaceExt --- mipidsi/src/builder.rs | 6 +++--- mipidsi/src/dcs.rs | 30 +++++++---------------------- mipidsi/src/graphics.rs | 7 ++++--- mipidsi/src/interface/parallel.rs | 4 ++-- mipidsi/src/lib.rs | 32 +++++++++++++++---------------- mipidsi/src/models.rs | 8 ++------ mipidsi/src/models/gc9a01.rs | 8 ++++---- mipidsi/src/models/ili9341.rs | 6 +++--- mipidsi/src/models/ili9342c.rs | 6 +++--- mipidsi/src/models/ili934x.rs | 4 ++-- mipidsi/src/models/ili9486.rs | 8 ++++---- mipidsi/src/models/st7735s.rs | 6 +++--- mipidsi/src/models/st7789.rs | 4 ++-- mipidsi/src/models/st7796.rs | 7 ++----- mipidsi/tests/external.rs | 4 ++-- 15 files changed, 59 insertions(+), 81 deletions(-) diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index c31ae84..a008247 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -4,7 +4,7 @@ use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; use crate::interface::{PixelFormat, PixelInterface}; -use crate::{dcs::Dcs, error::InitError, models::Model, Display}; +use crate::{dcs::InterfaceExt, error::InitError, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -160,7 +160,7 @@ where assert!(width + offset_x <= max_width); assert!(height + offset_y <= max_height); - let mut dcs = Dcs::write_only(self.di); + let mut dcs = self.di; match self.rst { Some(ref mut rst) => { @@ -179,7 +179,7 @@ where .map_err(InitError::DisplayError)?; let display = Display { - dcs, + di: dcs, model: self.model, rst: self.rst, options: self.options, diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index 7189437..bf412bb 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -42,27 +42,9 @@ pub trait DcsCommand { /// All other commands, which do not have an associated type in this module, can be sent using /// the [`write_raw`](Self::write_raw) method. The underlying display interface is also accessible /// using the public [`di`](Self::di) field. -pub struct Dcs { - /// Display interface instance. - pub di: DI, -} - -impl Dcs -where - DI: CommandInterface, -{ - /// Creates a new [Dcs] instance from a display interface. - pub fn write_only(di: DI) -> Self { - Self { di } - } - - /// Releases the display interface. - pub fn release(self) -> DI { - self.di - } - +pub trait InterfaceExt: CommandInterface { /// Sends a DCS command to the display interface. - pub fn write_command(&mut self, command: impl DcsCommand) -> Result<(), DI::Error> { + fn write_command(&mut self, command: impl DcsCommand) -> Result<(), Self::Error> { let mut param_bytes: [u8; 16] = [0; 16]; let n = command.fill_params_buf(&mut param_bytes); self.write_raw(command.instruction(), ¶m_bytes[..n]) @@ -77,12 +59,14 @@ where /// This method is intended to be used for sending commands which are not part of the MIPI DCS /// user command set. Use [`write_command`](Self::write_command) for commands in the user /// command set. - pub fn write_raw(&mut self, instruction: u8, param_bytes: &[u8]) -> Result<(), DI::Error> { - self.di.send_command(instruction, param_bytes)?; - self.di.flush() + fn write_raw(&mut self, instruction: u8, param_bytes: &[u8]) -> Result<(), Self::Error> { + self.send_command(instruction, param_bytes)?; + self.flush() } } +impl InterfaceExt for T {} + // DCS commands that don't use any parameters dcs_basic_command!( diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index c0d2f45..8a5e694 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -7,6 +7,7 @@ use embedded_graphics_core::{ }; use embedded_hal::digital::OutputPin; +use crate::dcs::InterfaceExt; use crate::{dcs::BitsPerPixel, interface::PixelInterface}; use crate::{dcs::WriteMemoryStart, models::Model}; use crate::{interface::PixelFormat, Display}; @@ -111,9 +112,9 @@ where let ey = bottom_right.y as u16; self.set_address_window(sx, sy, ex, ey)?; - self.dcs.write_command(WriteMemoryStart)?; - M::ColorFormat::send_repeated_pixel(&mut self.dcs.di, color, count)?; - self.dcs.di.flush() + self.di.write_command(WriteMemoryStart)?; + M::ColorFormat::send_repeated_pixel(&mut self.di, color, count)?; + self.di.flush() } } diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 93632a6..daf1398 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -238,7 +238,7 @@ where pixel: [Self::PixelWord; N], count: u32, ) -> Result<(), Self::Error> { - if count == 0 { + if count == 0 || N == 0 { return Ok(()); } @@ -347,7 +347,7 @@ where pixel: [Self::PixelWord; N], count: u32, ) -> Result<(), Self::Error> { - if count == 0 { + if count == 0 || N == 0 { return Ok(()); } diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 0aa2121..4125bc3 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -96,7 +96,7 @@ //! ## Troubleshooting //! See [document](https://github.com/almindor/mipidsi/blob/master/docs/TROUBLESHOOTING.md) -use dcs::Dcs; +use dcs::InterfaceExt; pub mod interface; @@ -136,7 +136,7 @@ where RST: OutputPin, { // DCS provider - dcs: Dcs, + di: DI, // Model model: MODEL, // Reset pin @@ -176,7 +176,7 @@ where /// ``` pub fn set_orientation(&mut self, orientation: options::Orientation) -> Result<(), DI::Error> { self.madctl = self.madctl.with_orientation(orientation); // set orientation - self.dcs.write_command(self.madctl)?; + self.di.write_command(self.madctl)?; Ok(()) } @@ -242,11 +242,11 @@ where { self.set_address_window(sx, sy, ex, ey)?; - self.dcs.write_command(dcs::WriteMemoryStart)?; + self.di.write_command(dcs::WriteMemoryStart)?; - M::ColorFormat::send_pixels(&mut self.dcs.di, colors.into_iter())?; + M::ColorFormat::send_pixels(&mut self.di, colors.into_iter())?; - self.dcs.di.flush() + self.di.flush() } /// Sets the vertical scroll region. @@ -281,7 +281,7 @@ where ) }; - self.dcs.write_command(vscrdef) + self.di.write_command(vscrdef) } /// Sets the vertical scroll offset. @@ -293,7 +293,7 @@ where /// using this method. pub fn set_vertical_scroll_offset(&mut self, offset: u16) -> Result<(), DI::Error> { let vscad = dcs::SetScrollStart::new(offset); - self.dcs.write_command(vscad) + self.di.write_command(vscad) } /// @@ -301,7 +301,7 @@ where /// This returns the display interface, reset pin and and the model deconstructing the driver. /// pub fn release(self) -> (DI, M, Option) { - (self.dcs.release(), self.model, self.rst) + (self.di, self.model, self.rst) } // Sets the address window for the display. @@ -321,8 +321,8 @@ where let (sx, sy, ex, ey) = (sx + offset.0, sy + offset.1, ex + offset.0, ey + offset.1); - self.dcs.write_command(dcs::SetColumnAddress::new(sx, ex))?; - self.dcs.write_command(dcs::SetPageAddress::new(sy, ey)) + self.di.write_command(dcs::SetColumnAddress::new(sx, ex))?; + self.di.write_command(dcs::SetPageAddress::new(sy, ey)) } /// @@ -332,7 +332,7 @@ where &mut self, tearing_effect: options::TearingEffect, ) -> Result<(), DI::Error> { - self.dcs + self.di .write_command(dcs::SetTearingEffect::new(tearing_effect)) } @@ -348,7 +348,7 @@ where /// Need to call [Self::wake] before issuing other commands /// pub fn sleep(&mut self, delay: &mut D) -> Result<(), DI::Error> { - self.dcs.write_command(dcs::EnterSleepMode)?; + self.di.write_command(dcs::EnterSleepMode)?; // All supported models requires a 120ms delay before issuing other commands delay.delay_us(120_000); self.sleeping = true; @@ -359,7 +359,7 @@ where /// Wakes the display after it's been set to sleep via [Self::sleep] /// pub fn wake(&mut self, delay: &mut D) -> Result<(), DI::Error> { - self.dcs.write_command(dcs::ExitSleepMode)?; + self.di.write_command(dcs::ExitSleepMode)?; // ST7789 and st7735s have the highest minimal delay of 120ms delay.delay_us(120_000); self.sleeping = false; @@ -374,8 +374,8 @@ where /// because the rest of the code isn't aware of any state changes that were caused by sending raw commands. /// The user must ensure that the state of the controller isn't altered in a way that interferes with the normal /// operation of this crate. - pub unsafe fn dcs(&mut self) -> &mut Dcs { - &mut self.dcs + pub unsafe fn dcs(&mut self) -> &mut DI { + &mut self.di } } diff --git a/mipidsi/src/models.rs b/mipidsi/src/models.rs index 5144c9b..7988a39 100644 --- a/mipidsi/src/models.rs +++ b/mipidsi/src/models.rs @@ -1,10 +1,6 @@ //! Display models. -use crate::{ - dcs::{Dcs, SetAddressMode}, - interface::CommandInterface, - options::ModelOptions, -}; +use crate::{dcs::SetAddressMode, interface::CommandInterface, options::ModelOptions}; use embedded_graphics_core::prelude::RgbColor; use embedded_hal::delay::DelayNs; @@ -38,7 +34,7 @@ pub trait Model { /// and returns the value of MADCTL set by init fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/gc9a01.rs b/mipidsi/src/models/gc9a01.rs index 680acbe..f888070 100644 --- a/mipidsi/src/models/gc9a01.rs +++ b/mipidsi/src/models/gc9a01.rs @@ -3,14 +3,14 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{ - BitsPerPixel, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, - SetPixelFormat, + BitsPerPixel, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, + SetInvertMode, SetPixelFormat, }, interface::CommandInterface, options::ModelOptions, }; -use super::{Dcs, Model}; +use super::Model; /// GC9A01 display in Rgb565 color mode. pub struct GC9A01; @@ -21,7 +21,7 @@ impl Model for GC9A01 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/ili9341.rs b/mipidsi/src/models/ili9341.rs index 7b2483a..14dbaf5 100644 --- a/mipidsi/src/models/ili9341.rs +++ b/mipidsi/src/models/ili9341.rs @@ -2,7 +2,7 @@ use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::delay::DelayNs; use crate::{ - dcs::{BitsPerPixel, Dcs, PixelFormat, SetAddressMode}, + dcs::{BitsPerPixel, PixelFormat, SetAddressMode}, interface::CommandInterface, models::{ili934x, Model}, options::ModelOptions, @@ -20,7 +20,7 @@ impl Model for ILI9341Rgb565 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -39,7 +39,7 @@ impl Model for ILI9341Rgb666 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/ili9342c.rs b/mipidsi/src/models/ili9342c.rs index e1fba04..284c476 100644 --- a/mipidsi/src/models/ili9342c.rs +++ b/mipidsi/src/models/ili9342c.rs @@ -2,7 +2,7 @@ use embedded_graphics_core::pixelcolor::{Rgb565, Rgb666}; use embedded_hal::delay::DelayNs; use crate::{ - dcs::{BitsPerPixel, Dcs, PixelFormat, SetAddressMode}, + dcs::{BitsPerPixel, PixelFormat, SetAddressMode}, interface::CommandInterface, models::{ili934x, Model}, options::ModelOptions, @@ -20,7 +20,7 @@ impl Model for ILI9342CRgb565 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -39,7 +39,7 @@ impl Model for ILI9342CRgb666 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/ili934x.rs b/mipidsi/src/models/ili934x.rs index f14275e..d7191a6 100644 --- a/mipidsi/src/models/ili934x.rs +++ b/mipidsi/src/models/ili934x.rs @@ -2,7 +2,7 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{ - Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, + EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, interface::CommandInterface, @@ -11,7 +11,7 @@ use crate::{ /// Common init for all ILI934x controllers and color formats. pub fn init_common( - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, diff --git a/mipidsi/src/models/ili9486.rs b/mipidsi/src/models/ili9486.rs index aa22e2f..6ea5993 100644 --- a/mipidsi/src/models/ili9486.rs +++ b/mipidsi/src/models/ili9486.rs @@ -3,7 +3,7 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{ - BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, + BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, interface::CommandInterface, @@ -24,7 +24,7 @@ impl Model for ILI9486Rgb565 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -45,7 +45,7 @@ impl Model for ILI9486Rgb666 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -62,7 +62,7 @@ impl Model for ILI9486Rgb666 { // common init for all color format models fn init_common( - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, diff --git a/mipidsi/src/models/st7735s.rs b/mipidsi/src/models/st7735s.rs index f6e741f..d184f83 100644 --- a/mipidsi/src/models/st7735s.rs +++ b/mipidsi/src/models/st7735s.rs @@ -3,8 +3,8 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{ - BitsPerPixel, Dcs, ExitSleepMode, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, - SetPixelFormat, + BitsPerPixel, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, + SetInvertMode, SetPixelFormat, }, interface::CommandInterface, models::Model, @@ -20,7 +20,7 @@ impl Model for ST7735s { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/st7789.rs b/mipidsi/src/models/st7789.rs index 0f182f8..3b2ca48 100644 --- a/mipidsi/src/models/st7789.rs +++ b/mipidsi/src/models/st7789.rs @@ -3,7 +3,7 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{ - BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, + BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, interface::CommandInterface, @@ -22,7 +22,7 @@ impl Model for ST7789 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/st7796.rs b/mipidsi/src/models/st7796.rs index 271cd31..e685b77 100644 --- a/mipidsi/src/models/st7796.rs +++ b/mipidsi/src/models/st7796.rs @@ -2,10 +2,7 @@ use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use crate::{ - dcs::{Dcs, SetAddressMode}, - interface::CommandInterface, - models::Model, - options::ModelOptions, + dcs::SetAddressMode, interface::CommandInterface, models::Model, options::ModelOptions, }; /// ST7796 display in Rgb565 color mode. @@ -19,7 +16,7 @@ impl Model for ST7796 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/tests/external.rs b/mipidsi/tests/external.rs index 1db8382..6e73942 100644 --- a/mipidsi/tests/external.rs +++ b/mipidsi/tests/external.rs @@ -2,7 +2,7 @@ use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; use mipidsi::{ dcs::{ - BitsPerPixel, Dcs, EnterNormalMode, ExitSleepMode, PixelFormat, SetAddressMode, + BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, interface::CommandInterface, @@ -20,7 +20,7 @@ impl Model for ExternalST7789 { fn init( &mut self, - dcs: &mut Dcs, + dcs: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result From 41c451cf0fa69257874ce6187979323f5c988953 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:19:58 -0600 Subject: [PATCH 13/36] Make parallel interface generic over word size --- mipidsi/src/interface/parallel.rs | 140 ++++-------------------------- 1 file changed, 17 insertions(+), 123 deletions(-) diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index daf1398..502a36b 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -146,133 +146,25 @@ pub enum ParallelError { Wr(WR), } -/// Parallel 8 Bit communication interface +/// Parallel communication interface /// -/// This interface implements an 8-Bit "8080" style write-only display interface using any -/// 8-bit [OutputBus] implementation as well as one +/// This interface implements a "8080" style write-only display interface using any +/// [OutputBus] implementation as well as one /// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. /// /// All pins are supposed to be high-active, high for the D/C pin meaning "data" and the /// write-enable being pulled low before the setting of the bits and supposed to be sampled at a /// low to high edge. -pub struct PGpio8BitInterface { +pub struct ParallelInterface { bus: BUS, dc: DC, wr: WR, } -impl PGpio8BitInterface +impl ParallelInterface where - BUS: OutputBus, - DC: OutputPin, - WR: OutputPin, -{ - /// Create new parallel GPIO interface for communication with a display driver - pub fn new(bus: BUS, dc: DC, wr: WR) -> Self { - Self { bus, dc, wr } - } - - /// Consume the display interface and return - /// the bus and GPIO pins used by it - pub fn release(self) -> (BUS, DC, WR) { - (self.bus, self.dc, self.wr) - } - - fn send_byte( - &mut self, - byte: u8, - ) -> Result<(), ParallelError> { - self.wr.set_low().map_err(ParallelError::Wr)?; - self.bus.set_value(byte).map_err(ParallelError::Bus)?; - self.wr.set_high().map_err(ParallelError::Wr) - } -} - -impl CommandInterface for PGpio8BitInterface -where - BUS: OutputBus, - DC: OutputPin, - WR: OutputPin, -{ - type Error = ParallelError; - - fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { - self.dc.set_low().map_err(ParallelError::Dc)?; - self.send_byte(command)?; - self.dc.set_high().map_err(ParallelError::Dc)?; - - for arg in args { - self.send_byte(*arg)?; - } - - Ok(()) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl PixelInterface for PGpio8BitInterface -where - BUS: OutputBus, - DC: OutputPin, - WR: OutputPin, -{ - type PixelWord = u8; - - fn send_pixels( - &mut self, - pixels: impl IntoIterator, - ) -> Result<(), Self::Error> { - for pixel in pixels { - for byte in pixel { - self.send_byte(byte)?; - } - } - Ok(()) - } - - fn send_repeated_pixel( - &mut self, - pixel: [Self::PixelWord; N], - count: u32, - ) -> Result<(), Self::Error> { - if count == 0 || N == 0 { - return Ok(()); - } - - if let Some(word) = is_same(pixel) { - self.send_byte(word)?; - for _ in 1..(count * N as u32) { - self.wr.set_low().map_err(ParallelError::Wr)?; - self.wr.set_high().map_err(ParallelError::Wr)?; - } - Ok(()) - } else { - self.send_pixels((0..count).map(|_| pixel)) - } - } -} - -/// Parallel 16 Bit communication interface -/// -/// This interface implements a 16-Bit "8080" style write-only display interface using any -/// 16-bit [OutputBus] implementation as well as one -/// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. -/// -/// All pins are supposed to be high-active, high for the D/C pin meaning "data" and the -/// write-enable being pulled low before the setting of the bits and supposed to be sampled at a -/// low to high edge. -pub struct PGpio16BitInterface { - bus: BUS, - dc: DC, - wr: WR, -} - -impl PGpio16BitInterface -where - BUS: OutputBus, + BUS: OutputBus, + BUS::Word: From + Eq, DC: OutputPin, WR: OutputPin, { @@ -289,7 +181,7 @@ where fn send_word( &mut self, - word: u16, + word: BUS::Word, ) -> Result<(), ParallelError> { self.wr.set_low().map_err(ParallelError::Wr)?; self.bus.set_value(word).map_err(ParallelError::Bus)?; @@ -297,9 +189,10 @@ where } } -impl CommandInterface for PGpio16BitInterface +impl CommandInterface for ParallelInterface where - BUS: OutputBus, + BUS: OutputBus, + BUS::Word: From + Eq, DC: OutputPin, WR: OutputPin, { @@ -307,11 +200,11 @@ where fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { self.dc.set_low().map_err(ParallelError::Dc)?; - self.send_word(u16::from(command))?; + self.send_word(BUS::Word::from(command))?; self.dc.set_high().map_err(ParallelError::Dc)?; for arg in args { - self.send_word(u16::from(*arg))?; + self.send_word(BUS::Word::from(*arg))?; } Ok(()) @@ -322,13 +215,14 @@ where } } -impl PixelInterface for PGpio16BitInterface +impl PixelInterface for ParallelInterface where - BUS: OutputBus, + BUS: OutputBus, + BUS::Word: From + Eq, DC: OutputPin, WR: OutputPin, { - type PixelWord = u16; + type PixelWord = BUS::Word; fn send_pixels( &mut self, From 7bc71455fb8e74d47bfcd3b72170926513aaf47f Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:30:48 -0600 Subject: [PATCH 14/36] Fix docs --- mipidsi/src/dcs.rs | 3 +-- mipidsi/src/lib.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index bf412bb..513e2f7 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -40,8 +40,7 @@ pub trait DcsCommand { /// in this module. /// /// All other commands, which do not have an associated type in this module, can be sent using -/// the [`write_raw`](Self::write_raw) method. The underlying display interface is also accessible -/// using the public [`di`](Self::di) field. +/// the [`write_raw`](Self::write_raw) method. pub trait InterfaceExt: CommandInterface { /// Sends a DCS command to the display interface. fn write_command(&mut self, command: impl DcsCommand) -> Result<(), Self::Error> { diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 4125bc3..e49a4d8 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -23,7 +23,7 @@ //! ## Examples //! **For the ili9486 display, using the SPI interface with no chip select:** //! ``` -//! use display_interface_spi::SPIInterface; // Provides the builder for DisplayInterface +//! use mipidsi::interface::SpiInterface; // Provides the builder for DisplayInterface //! use mipidsi::{Builder, models::ILI9486Rgb666}; // Provides the builder for Display //! use embedded_graphics::{prelude::*, pixelcolor::Rgb666}; // Provides the required color type //! @@ -35,8 +35,11 @@ //!# let rst = mipidsi::_mock::MockOutputPin; //!# let mut delay = mipidsi::_mock::MockDelay; //! +//! // Create a buffer +//! let mut buffer = [0_u8; 512]; +//! //! // Create a DisplayInterface from SPI and DC pin, with no manual CS control -//! let di = SPIInterface::new(spi, dc); +//! let di = SpiInterface::new(spi, dc, &mut buffer); //! //! // Create the ILI9486 display driver from the display interface and optional RST pin //! let mut display = Builder::new(ILI9486Rgb666, di) @@ -51,7 +54,7 @@ //! color order:** //! ``` //! // Provides the builder for DisplayInterface -//! use display_interface_parallel_gpio::{Generic8BitBus, PGPIO8BitInterface}; +//! use mipidsi::interface::{Generic8BitBus, ParallelInterface}; //! // Provides the builder for Display //! use mipidsi::{Builder, models::ILI9341Rgb666}; //! // Provides the required color type @@ -77,7 +80,7 @@ //! // Create the DisplayInterface from a Generic8BitBus, which is made from the parallel pins //! let bus = Generic8BitBus::new((lcd_d0, lcd_d1, lcd_d2, //! lcd_d3, lcd_d4, lcd_d5, lcd_d6, lcd_d7)); -//! let di = PGPIO8BitInterface::new(bus, dc, wr); +//! let di = ParallelInterface::new(bus, dc, wr); //! //! // Create the ILI9341 display driver from the display interface with the RGB666 color space //! let mut display = Builder::new(ILI9341Rgb666, di) From 84a2ca6fe47b3253f4de78595b0f5ce84305b749 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:39:03 -0600 Subject: [PATCH 15/36] Rearrange spi --- mipidsi/src/interface/spi.rs | 93 ++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 821d338..439fc85 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -23,36 +23,32 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { Self { spi, dc, buffer } } +} - fn push_bytes_repeated( - &mut self, - bytes: [u8; N], - count: u32, - ) -> Result<(), SPI::Error> { - let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); - let filled_len = fill_count as usize * N; - for chunk in self.buffer[..(filled_len)].chunks_exact_mut(N) { - let chunk: &mut [u8; N] = chunk.try_into().unwrap(); - *chunk = bytes; - } +impl CommandInterface for SpiInterface<'_, SPI, DC> { + type Error = SpiError; - let mut count = count; - while count >= fill_count { - self.spi.write(&self.buffer[..filled_len])?; - count -= fill_count; - } - if count != 0 { - self.spi - .write(&self.buffer[..(count as usize * bytes.len())])?; - } + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + self.dc.set_low().map_err(SpiError::Dc)?; + self.spi.write(&[command]).map_err(SpiError::Spi)?; + self.dc.set_high().map_err(SpiError::Dc)?; + self.spi.write(args).map_err(SpiError::Spi)?; Ok(()) } - fn push_array_iter( + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl PixelInterface for SpiInterface<'_, SPI, DC> { + type PixelWord = u8; + + fn send_pixels( &mut self, - arrays: impl IntoIterator, - ) -> Result<(), SPI::Error> { - let mut arrays = arrays.into_iter(); + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { + let mut arrays = pixels.into_iter(); assert!(self.buffer.len() >= N); @@ -69,44 +65,35 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { break; }; } - self.spi.write(&self.buffer[..i])?; + self.spi.write(&self.buffer[..i]).map_err(SpiError::Spi)?; } Ok(()) } -} - -impl CommandInterface for SpiInterface<'_, SPI, DC> { - type Error = SpiError; - - fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { - self.dc.set_low().map_err(SpiError::Dc)?; - self.spi.write(&[command]).map_err(SpiError::Spi)?; - self.dc.set_high().map_err(SpiError::Dc)?; - self.spi.write(args).map_err(SpiError::Spi)?; - Ok(()) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl PixelInterface for SpiInterface<'_, SPI, DC> { - type PixelWord = u8; fn send_repeated_pixel( &mut self, pixel: [Self::PixelWord; N], count: u32, ) -> Result<(), Self::Error> { - self.push_bytes_repeated(pixel, count) - .map_err(SpiError::Spi) - } + let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); + let filled_len = fill_count as usize * N; + for chunk in self.buffer[..(filled_len)].chunks_exact_mut(N) { + let chunk: &mut [u8; N] = chunk.try_into().unwrap(); + *chunk = pixel; + } - fn send_pixels( - &mut self, - pixels: impl IntoIterator, - ) -> Result<(), Self::Error> { - self.push_array_iter(pixels).map_err(SpiError::Spi) + let mut count = count; + while count >= fill_count { + self.spi + .write(&self.buffer[..filled_len]) + .map_err(SpiError::Spi)?; + count -= fill_count; + } + if count != 0 { + self.spi + .write(&self.buffer[..(count as usize * pixel.len())]) + .map_err(SpiError::Spi)?; + } + Ok(()) } } From 5e23125bb50f9db604bed6c2d1c634f57bad4c35 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:42:03 -0600 Subject: [PATCH 16/36] Impl for &mut T --- mipidsi/src/interface/mod.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index e93b983..22a112b 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -51,6 +51,37 @@ pub trait PixelInterface: CommandInterface { ) -> Result<(), Self::Error>; } +impl CommandInterface for &mut T { + type Error = T::Error; + + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + T::send_command(self, command, args) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } +} + +impl PixelInterface for &mut T { + type PixelWord = T::PixelWord; + + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { + T::send_pixels(self, pixels) + } + + fn send_repeated_pixel( + &mut self, + pixel: [Self::PixelWord; N], + count: u32, + ) -> Result<(), Self::Error> { + T::send_repeated_pixel(self, pixel, count) + } +} + fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { embedded_graphics_core::pixelcolor::raw::ToBytes::to_be_bytes(pixel) } From 55cda09d75984c7056abb83e890fbc0293114761 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 10:50:37 -0600 Subject: [PATCH 17/36] Rename dcs to di --- mipidsi/src/builder.rs | 9 ++- mipidsi/src/interface/mod.rs | 60 +++++++++---------- mipidsi/src/models.rs | 2 +- mipidsi/src/models/gc9a01.rs | 106 ++++++++++++++++----------------- mipidsi/src/models/ili9341.rs | 8 +-- mipidsi/src/models/ili9342c.rs | 8 +-- mipidsi/src/models/ili934x.rs | 16 ++--- mipidsi/src/models/ili9486.rs | 28 ++++----- mipidsi/src/models/st7735s.rs | 36 +++++------ mipidsi/src/models/st7789.rs | 14 ++--- mipidsi/src/models/st7796.rs | 4 +- mipidsi/tests/external.rs | 14 ++--- 12 files changed, 152 insertions(+), 153 deletions(-) diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index a008247..e9e2071 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -160,26 +160,25 @@ where assert!(width + offset_x <= max_width); assert!(height + offset_y <= max_height); - let mut dcs = self.di; - match self.rst { Some(ref mut rst) => { rst.set_low().map_err(InitError::Pin)?; delay_source.delay_us(10); rst.set_high().map_err(InitError::Pin)?; } - None => dcs + None => self + .di .write_command(crate::dcs::SoftReset) .map_err(InitError::DisplayError)?, } let madctl = self .model - .init(&mut dcs, delay_source, &self.options) + .init(&mut self.di, delay_source, &self.options) .map_err(InitError::DisplayError)?; let display = Display { - di: dcs, + di: self.di, model: self.model, rst: self.rst, options: self.options, diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 22a112b..0cd6719 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -102,66 +102,66 @@ pub trait PixelFormat { // but that doesn't work yet #[doc(hidden)] - fn send_pixels>( - dcs: &mut DCS, + fn send_pixels>( + di: &mut DI, pixels: impl IntoIterator, - ) -> Result<(), DCS::Error>; + ) -> Result<(), DI::Error>; #[doc(hidden)] - fn send_repeated_pixel>( - dcs: &mut DCS, + fn send_repeated_pixel>( + di: &mut DI, pixel: Self, count: u32, - ) -> Result<(), DCS::Error>; + ) -> Result<(), DI::Error>; } impl PixelFormat for Rgb565 { - fn send_pixels>( - dcs: &mut DCS, + fn send_pixels>( + di: &mut DI, pixels: impl IntoIterator, - ) -> Result<(), DCS::Error> { - dcs.send_pixels(pixels.into_iter().map(rgb565_to_bytes)) + ) -> Result<(), DI::Error> { + di.send_pixels(pixels.into_iter().map(rgb565_to_bytes)) } - fn send_repeated_pixel>( - dcs: &mut DCS, + fn send_repeated_pixel>( + di: &mut DI, pixel: Self, count: u32, - ) -> Result<(), DCS::Error> { - dcs.send_repeated_pixel(rgb565_to_bytes(pixel), count) + ) -> Result<(), DI::Error> { + di.send_repeated_pixel(rgb565_to_bytes(pixel), count) } } impl PixelFormat for Rgb666 { - fn send_pixels>( - dcs: &mut DCS, + fn send_pixels>( + di: &mut DI, pixels: impl IntoIterator, - ) -> Result<(), DCS::Error> { - dcs.send_pixels(pixels.into_iter().map(rgb666_to_bytes)) + ) -> Result<(), DI::Error> { + di.send_pixels(pixels.into_iter().map(rgb666_to_bytes)) } - fn send_repeated_pixel>( - dcs: &mut DCS, + fn send_repeated_pixel>( + di: &mut DI, pixel: Self, count: u32, - ) -> Result<(), DCS::Error> { - dcs.send_repeated_pixel(rgb666_to_bytes(pixel), count) + ) -> Result<(), DI::Error> { + di.send_repeated_pixel(rgb666_to_bytes(pixel), count) } } impl PixelFormat for Rgb565 { - fn send_pixels>( - dcs: &mut DCS, + fn send_pixels>( + di: &mut DI, pixels: impl IntoIterator, - ) -> Result<(), DCS::Error> { - dcs.send_pixels(pixels.into_iter().map(rgb565_to_u16)) + ) -> Result<(), DI::Error> { + di.send_pixels(pixels.into_iter().map(rgb565_to_u16)) } - fn send_repeated_pixel>( - dcs: &mut DCS, + fn send_repeated_pixel>( + di: &mut DI, pixel: Self, count: u32, - ) -> Result<(), DCS::Error> { - dcs.send_repeated_pixel(rgb565_to_u16(pixel), count) + ) -> Result<(), DI::Error> { + di.send_repeated_pixel(rgb565_to_u16(pixel), count) } } diff --git a/mipidsi/src/models.rs b/mipidsi/src/models.rs index 7988a39..0c0add6 100644 --- a/mipidsi/src/models.rs +++ b/mipidsi/src/models.rs @@ -34,7 +34,7 @@ pub trait Model { /// and returns the value of MADCTL set by init fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result diff --git a/mipidsi/src/models/gc9a01.rs b/mipidsi/src/models/gc9a01.rs index f888070..19590a3 100644 --- a/mipidsi/src/models/gc9a01.rs +++ b/mipidsi/src/models/gc9a01.rs @@ -21,7 +21,7 @@ impl Model for GC9A01 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -33,92 +33,92 @@ impl Model for GC9A01 { delay.delay_us(200_000); - dcs.write_raw(0xEF, &[])?; // inter register enable 2 - dcs.write_raw(0xEB, &[0x14])?; - dcs.write_raw(0xFE, &[])?; // inter register enable 1 - dcs.write_raw(0xEF, &[])?; // inter register enable 2 - dcs.write_raw(0xEB, &[0x14])?; - - dcs.write_raw(0x84, &[0x40])?; - dcs.write_raw(0x85, &[0xFF])?; - dcs.write_raw(0x86, &[0xFF])?; - dcs.write_raw(0x87, &[0xFF])?; - dcs.write_raw(0x88, &[0x0A])?; - dcs.write_raw(0x89, &[0x21])?; - dcs.write_raw(0x8A, &[0x00])?; - dcs.write_raw(0x8B, &[0x80])?; - dcs.write_raw(0x8C, &[0x01])?; - dcs.write_raw(0x8D, &[0x01])?; - dcs.write_raw(0x8E, &[0xFF])?; - dcs.write_raw(0x8F, &[0xFF])?; - - dcs.write_raw(0xB6, &[0x00, 0x20])?; // display function control - - dcs.write_command(madctl)?; // set memory data access control, Top -> Bottom, RGB, Left -> Right + di.write_raw(0xEF, &[])?; // inter register enable 2 + di.write_raw(0xEB, &[0x14])?; + di.write_raw(0xFE, &[])?; // inter register enable 1 + di.write_raw(0xEF, &[])?; // inter register enable 2 + di.write_raw(0xEB, &[0x14])?; + + di.write_raw(0x84, &[0x40])?; + di.write_raw(0x85, &[0xFF])?; + di.write_raw(0x86, &[0xFF])?; + di.write_raw(0x87, &[0xFF])?; + di.write_raw(0x88, &[0x0A])?; + di.write_raw(0x89, &[0x21])?; + di.write_raw(0x8A, &[0x00])?; + di.write_raw(0x8B, &[0x80])?; + di.write_raw(0x8C, &[0x01])?; + di.write_raw(0x8D, &[0x01])?; + di.write_raw(0x8E, &[0xFF])?; + di.write_raw(0x8F, &[0xFF])?; + + di.write_raw(0xB6, &[0x00, 0x20])?; // display function control + + di.write_command(madctl)?; // set memory data access control, Top -> Bottom, RGB, Left -> Right let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - dcs.write_command(SetPixelFormat::new(pf))?; // set interface pixel format, 16bit pixel into frame memory + di.write_command(SetPixelFormat::new(pf))?; // set interface pixel format, 16bit pixel into frame memory - dcs.write_raw(0x90, &[0x08, 0x08, 0x08, 0x08])?; - dcs.write_raw(0xBD, &[0x06])?; - dcs.write_raw(0xBC, &[0x00])?; - dcs.write_raw(0xFF, &[0x60, 0x01, 0x04])?; + di.write_raw(0x90, &[0x08, 0x08, 0x08, 0x08])?; + di.write_raw(0xBD, &[0x06])?; + di.write_raw(0xBC, &[0x00])?; + di.write_raw(0xFF, &[0x60, 0x01, 0x04])?; - dcs.write_raw(0xC3, &[0x13])?; // power control 2 - dcs.write_raw(0xC4, &[0x13])?; // power control 3 - dcs.write_raw(0xC9, &[0x22])?; // power control 4 + di.write_raw(0xC3, &[0x13])?; // power control 2 + di.write_raw(0xC4, &[0x13])?; // power control 3 + di.write_raw(0xC9, &[0x22])?; // power control 4 - dcs.write_raw(0xBE, &[0x11])?; - dcs.write_raw(0xE1, &[0x10, 0x0E])?; - dcs.write_raw(0xDF, &[0x20, 0x0c, 0x02])?; + di.write_raw(0xBE, &[0x11])?; + di.write_raw(0xE1, &[0x10, 0x0E])?; + di.write_raw(0xDF, &[0x20, 0x0c, 0x02])?; - dcs.write_raw(0xF0, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; // gamma 1 - dcs.write_raw(0xF1, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6f])?; // gamma 2 - dcs.write_raw(0xF2, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; // gamma 3 - dcs.write_raw(0xF3, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6f])?; // gamma 4 + di.write_raw(0xF0, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; // gamma 1 + di.write_raw(0xF1, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6f])?; // gamma 2 + di.write_raw(0xF2, &[0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])?; // gamma 3 + di.write_raw(0xF3, &[0x43, 0x70, 0x72, 0x36, 0x37, 0x6f])?; // gamma 4 - dcs.write_raw(0xED, &[0x18, 0x0B])?; - dcs.write_raw(0xAE, &[0x77])?; - dcs.write_raw(0xCD, &[0x63])?; + di.write_raw(0xED, &[0x18, 0x0B])?; + di.write_raw(0xAE, &[0x77])?; + di.write_raw(0xCD, &[0x63])?; - dcs.write_raw( + di.write_raw( 0x70, &[0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03], )?; - dcs.write_raw(0xE8, &[0x34])?; // framerate + di.write_raw(0xE8, &[0x34])?; // framerate - dcs.write_raw( + di.write_raw( 0x62, &[ 0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70, ], )?; - dcs.write_raw( + di.write_raw( 0x63, &[ 0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70, ], )?; - dcs.write_raw(0x64, &[0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07])?; - dcs.write_raw( + di.write_raw(0x64, &[0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07])?; + di.write_raw( 0x66, &[0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00], )?; - dcs.write_raw( + di.write_raw( 0x67, &[0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98], )?; - dcs.write_raw(0x74, &[0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00])?; - dcs.write_raw(0x98, &[0x3e, 0x07])?; + di.write_raw(0x74, &[0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00])?; + di.write_raw(0x98, &[0x3e, 0x07])?; - dcs.write_command(SetInvertMode::new(options.invert_colors))?; // set color inversion + di.write_command(SetInvertMode::new(options.invert_colors))?; // set color inversion - dcs.write_command(ExitSleepMode)?; // turn off sleep + di.write_command(ExitSleepMode)?; // turn off sleep delay.delay_us(120_000); - dcs.write_command(SetDisplayOn)?; // turn on display + di.write_command(SetDisplayOn)?; // turn on display Ok(madctl) } diff --git a/mipidsi/src/models/ili9341.rs b/mipidsi/src/models/ili9341.rs index 14dbaf5..8199433 100644 --- a/mipidsi/src/models/ili9341.rs +++ b/mipidsi/src/models/ili9341.rs @@ -20,7 +20,7 @@ impl Model for ILI9341Rgb565 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -29,7 +29,7 @@ impl Model for ILI9341Rgb565 { DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) + ili934x::init_common(di, delay, options, pf).map_err(Into::into) } } @@ -39,7 +39,7 @@ impl Model for ILI9341Rgb666 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -48,6 +48,6 @@ impl Model for ILI9341Rgb666 { DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) + ili934x::init_common(di, delay, options, pf).map_err(Into::into) } } diff --git a/mipidsi/src/models/ili9342c.rs b/mipidsi/src/models/ili9342c.rs index 284c476..29be67a 100644 --- a/mipidsi/src/models/ili9342c.rs +++ b/mipidsi/src/models/ili9342c.rs @@ -20,7 +20,7 @@ impl Model for ILI9342CRgb565 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -29,7 +29,7 @@ impl Model for ILI9342CRgb565 { DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) + ili934x::init_common(di, delay, options, pf).map_err(Into::into) } } @@ -39,7 +39,7 @@ impl Model for ILI9342CRgb666 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -48,6 +48,6 @@ impl Model for ILI9342CRgb666 { DI: CommandInterface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - ili934x::init_common(dcs, delay, options, pf).map_err(Into::into) + ili934x::init_common(di, delay, options, pf).map_err(Into::into) } } diff --git a/mipidsi/src/models/ili934x.rs b/mipidsi/src/models/ili934x.rs index d7191a6..17df36c 100644 --- a/mipidsi/src/models/ili934x.rs +++ b/mipidsi/src/models/ili934x.rs @@ -11,7 +11,7 @@ use crate::{ /// Common init for all ILI934x controllers and color formats. pub fn init_common( - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, @@ -26,25 +26,25 @@ where // 8.2.2: It will be necessary to wait 5msec before sending new command following software reset. delay.delay_us(5_000); - dcs.write_command(madctl)?; - dcs.write_raw(0xB4, &[0x0])?; - dcs.write_command(SetInvertMode::new(options.invert_colors))?; - dcs.write_command(SetPixelFormat::new(pixel_format))?; + di.write_command(madctl)?; + di.write_raw(0xB4, &[0x0])?; + di.write_command(SetInvertMode::new(options.invert_colors))?; + di.write_command(SetPixelFormat::new(pixel_format))?; - dcs.write_command(EnterNormalMode)?; + di.write_command(EnterNormalMode)?; // 8.2.12: It will be necessary to wait 120msec after sending Sleep In command (when in Sleep Out mode) // before Sleep Out command can be sent. // The reset might have implicitly called the Sleep In command if the controller is reinitialized. delay.delay_us(120_000); - dcs.write_command(ExitSleepMode)?; + di.write_command(ExitSleepMode)?; // 8.2.12: It takes 120msec to become Sleep Out mode after SLPOUT command issued. // 13.2 Power ON Sequence: Delay should be 60ms + 80ms delay.delay_us(140_000); - dcs.write_command(SetDisplayOn)?; + di.write_command(SetDisplayOn)?; Ok(madctl) } diff --git a/mipidsi/src/models/ili9486.rs b/mipidsi/src/models/ili9486.rs index 6ea5993..471184e 100644 --- a/mipidsi/src/models/ili9486.rs +++ b/mipidsi/src/models/ili9486.rs @@ -24,7 +24,7 @@ impl Model for ILI9486Rgb565 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -35,7 +35,7 @@ impl Model for ILI9486Rgb565 { delay.delay_us(120_000); let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - init_common(dcs, delay, options, pf) + init_common(di, delay, options, pf) } } @@ -45,7 +45,7 @@ impl Model for ILI9486Rgb666 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -56,13 +56,13 @@ impl Model for ILI9486Rgb666 { delay.delay_us(120_000); let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - init_common(dcs, delay, options, pf) + init_common(di, delay, options, pf) } } // common init for all color format models fn init_common( - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, pixel_format: PixelFormat, @@ -72,20 +72,20 @@ where DI: CommandInterface, { let madctl = SetAddressMode::from(options); - dcs.write_command(ExitSleepMode)?; // turn off sleep - dcs.write_command(SetPixelFormat::new(pixel_format))?; // pixel format - dcs.write_command(madctl)?; // left -> right, bottom -> top RGB - // dcs.write_command(Instruction::VCMOFSET, &[0x00, 0x48, 0x00, 0x48])?; //VCOM Control 1 [00 40 00 40] - // dcs.write_command(Instruction::INVCO, &[0x0])?; //Inversion Control [00] - dcs.write_command(SetInvertMode::new(options.invert_colors))?; + di.write_command(ExitSleepMode)?; // turn off sleep + di.write_command(SetPixelFormat::new(pixel_format))?; // pixel format + di.write_command(madctl)?; // left -> right, bottom -> top RGB + // dcs.write_command(Instruction::VCMOFSET, &[0x00, 0x48, 0x00, 0x48])?; //VCOM Control 1 [00 40 00 40] + // dcs.write_command(Instruction::INVCO, &[0x0])?; //Inversion Control [00] + di.write_command(SetInvertMode::new(options.invert_colors))?; // optional gamma setup // dcs.write_raw(Instruction::PGC, &[0x00, 0x2C, 0x2C, 0x0B, 0x0C, 0x04, 0x4C, 0x64, 0x36, 0x03, 0x0E, 0x01, 0x10, 0x01, 0x00])?; // Positive Gamma Control // dcs.write_raw(Instruction::NGC, &[0x0F, 0x37, 0x37, 0x0C, 0x0F, 0x05, 0x50, 0x32, 0x36, 0x04, 0x0B, 0x00, 0x19, 0x14, 0x0F])?; // Negative Gamma Control - dcs.write_raw(0xB6, &[0b0000_0010, 0x02, 0x3B])?; // DFC - dcs.write_command(EnterNormalMode)?; // turn to normal mode - dcs.write_command(SetDisplayOn)?; // turn on display + di.write_raw(0xB6, &[0b0000_0010, 0x02, 0x3B])?; // DFC + di.write_command(EnterNormalMode)?; // turn to normal mode + di.write_command(SetDisplayOn)?; // turn on display // DISPON requires some time otherwise we risk SPI data issues delay.delay_us(120_000); diff --git a/mipidsi/src/models/st7735s.rs b/mipidsi/src/models/st7735s.rs index d184f83..576f0f4 100644 --- a/mipidsi/src/models/st7735s.rs +++ b/mipidsi/src/models/st7735s.rs @@ -20,7 +20,7 @@ impl Model for ST7735s { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -32,28 +32,28 @@ impl Model for ST7735s { delay.delay_us(200_000); - dcs.write_command(ExitSleepMode)?; // turn off sleep + di.write_command(ExitSleepMode)?; // turn off sleep delay.delay_us(120_000); - dcs.write_command(SetInvertMode::new(options.invert_colors))?; // set color inversion - dcs.write_raw(0xB1, &[0x05, 0x3A, 0x3A])?; // set frame rate - dcs.write_raw(0xB2, &[0x05, 0x3A, 0x3A])?; // set frame rate - dcs.write_raw(0xB3, &[0x05, 0x3A, 0x3A, 0x05, 0x3A, 0x3A])?; // set frame rate - dcs.write_raw(0xB4, &[0b0000_0011])?; // set inversion control - dcs.write_raw(0xC0, &[0x62, 0x02, 0x04])?; // set power control 1 - dcs.write_raw(0xC1, &[0xC0])?; // set power control 2 - dcs.write_raw(0xC2, &[0x0D, 0x00])?; // set power control 3 - dcs.write_raw(0xC3, &[0x8D, 0x6A])?; // set power control 4 - dcs.write_raw(0xC4, &[0x8D, 0xEE])?; // set power control 5 - dcs.write_raw(0xC5, &[0x0E])?; // set VCOM control 1 - dcs.write_raw( + di.write_command(SetInvertMode::new(options.invert_colors))?; // set color inversion + di.write_raw(0xB1, &[0x05, 0x3A, 0x3A])?; // set frame rate + di.write_raw(0xB2, &[0x05, 0x3A, 0x3A])?; // set frame rate + di.write_raw(0xB3, &[0x05, 0x3A, 0x3A, 0x05, 0x3A, 0x3A])?; // set frame rate + di.write_raw(0xB4, &[0b0000_0011])?; // set inversion control + di.write_raw(0xC0, &[0x62, 0x02, 0x04])?; // set power control 1 + di.write_raw(0xC1, &[0xC0])?; // set power control 2 + di.write_raw(0xC2, &[0x0D, 0x00])?; // set power control 3 + di.write_raw(0xC3, &[0x8D, 0x6A])?; // set power control 4 + di.write_raw(0xC4, &[0x8D, 0xEE])?; // set power control 5 + di.write_raw(0xC5, &[0x0E])?; // set VCOM control 1 + di.write_raw( 0xE0, &[ 0x10, 0x0E, 0x02, 0x03, 0x0E, 0x07, 0x02, 0x07, 0x0A, 0x12, 0x27, 0x37, 0x00, 0x0D, 0x0E, 0x10, ], )?; // set GAMMA +Polarity characteristics - dcs.write_raw( + di.write_raw( 0xE1, &[ 0x10, 0x0E, 0x03, 0x03, 0x0F, 0x06, 0x02, 0x08, 0x0A, 0x13, 0x26, 0x36, 0x00, 0x0D, @@ -62,10 +62,10 @@ impl Model for ST7735s { )?; // set GAMMA -Polarity characteristics let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - dcs.write_command(SetPixelFormat::new(pf))?; // set interface pixel format, 16bit pixel into frame memory + di.write_command(SetPixelFormat::new(pf))?; // set interface pixel format, 16bit pixel into frame memory - dcs.write_command(madctl)?; // set memory data access control, Top -> Bottom, RGB, Left -> Right - dcs.write_command(SetDisplayOn)?; // turn on display + di.write_command(madctl)?; // set memory data access control, Top -> Bottom, RGB, Left -> Right + di.write_command(SetDisplayOn)?; // turn on display Ok(madctl) } diff --git a/mipidsi/src/models/st7789.rs b/mipidsi/src/models/st7789.rs index 3b2ca48..ff59b67 100644 --- a/mipidsi/src/models/st7789.rs +++ b/mipidsi/src/models/st7789.rs @@ -22,7 +22,7 @@ impl Model for ST7789 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -34,20 +34,20 @@ impl Model for ST7789 { delay.delay_us(150_000); - dcs.write_command(ExitSleepMode)?; + di.write_command(ExitSleepMode)?; delay.delay_us(10_000); // set hw scroll area based on framebuffer size - dcs.write_command(madctl)?; + di.write_command(madctl)?; - dcs.write_command(SetInvertMode::new(options.invert_colors))?; + di.write_command(SetInvertMode::new(options.invert_colors))?; let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - dcs.write_command(SetPixelFormat::new(pf))?; + di.write_command(SetPixelFormat::new(pf))?; delay.delay_us(10_000); - dcs.write_command(EnterNormalMode)?; + di.write_command(EnterNormalMode)?; delay.delay_us(10_000); - dcs.write_command(SetDisplayOn)?; + di.write_command(SetDisplayOn)?; // DISPON requires some time otherwise we risk SPI data issues delay.delay_us(120_000); diff --git a/mipidsi/src/models/st7796.rs b/mipidsi/src/models/st7796.rs index e685b77..2fd4f47 100644 --- a/mipidsi/src/models/st7796.rs +++ b/mipidsi/src/models/st7796.rs @@ -16,7 +16,7 @@ impl Model for ST7796 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -24,6 +24,6 @@ impl Model for ST7796 { DELAY: DelayNs, DI: CommandInterface, { - super::ST7789.init(dcs, delay, options) + super::ST7789.init(di, delay, options) } } diff --git a/mipidsi/tests/external.rs b/mipidsi/tests/external.rs index 6e73942..6795fc4 100644 --- a/mipidsi/tests/external.rs +++ b/mipidsi/tests/external.rs @@ -20,7 +20,7 @@ impl Model for ExternalST7789 { fn init( &mut self, - dcs: &mut DI, + di: &mut DI, delay: &mut DELAY, options: &ModelOptions, ) -> Result @@ -32,20 +32,20 @@ impl Model for ExternalST7789 { delay.delay_us(150_000); - dcs.write_command(ExitSleepMode)?; + di.write_command(ExitSleepMode)?; delay.delay_us(10_000); // set hw scroll area based on framebuffer size - dcs.write_command(madctl)?; + di.write_command(madctl)?; - dcs.write_command(SetInvertMode::new(options.invert_colors))?; + di.write_command(SetInvertMode::new(options.invert_colors))?; let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); - dcs.write_command(SetPixelFormat::new(pf))?; + di.write_command(SetPixelFormat::new(pf))?; delay.delay_us(10_000); - dcs.write_command(EnterNormalMode)?; + di.write_command(EnterNormalMode)?; delay.delay_us(10_000); - dcs.write_command(SetDisplayOn)?; + di.write_command(SetDisplayOn)?; // DISPON requires some time otherwise we risk SPI data issues delay.delay_us(120_000); From 566155270d9b26f9c162366157d807c784580499 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 23 Nov 2024 11:12:36 -0600 Subject: [PATCH 18/36] Fix examples --- mipidsi/examples/parallel_ili9341_rp_pico/Cargo.toml | 1 - mipidsi/examples/parallel_ili9341_rp_pico/src/main.rs | 4 ++-- mipidsi/examples/spi-ili9486-esp32-c3/Cargo.toml | 8 ++++++-- mipidsi/examples/spi-ili9486-esp32-c3/src/main.rs | 6 ++++-- mipidsi/examples/spi-st7789-rpi-zero-w/Cargo.toml | 1 - mipidsi/examples/spi-st7789-rpi-zero-w/src/main.rs | 5 +++-- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/mipidsi/examples/parallel_ili9341_rp_pico/Cargo.toml b/mipidsi/examples/parallel_ili9341_rp_pico/Cargo.toml index 29cb362..fb061c7 100644 --- a/mipidsi/examples/parallel_ili9341_rp_pico/Cargo.toml +++ b/mipidsi/examples/parallel_ili9341_rp_pico/Cargo.toml @@ -16,7 +16,6 @@ rp-pico = "0.9.0" embedded-graphics = "0.8.0" embedded-graphics-core = "0.4.0" -display-interface-parallel-gpio = "0.7.0" mipidsi = { path = "../../" } [workspace] diff --git a/mipidsi/examples/parallel_ili9341_rp_pico/src/main.rs b/mipidsi/examples/parallel_ili9341_rp_pico/src/main.rs index 8e3ff92..39241b3 100644 --- a/mipidsi/examples/parallel_ili9341_rp_pico/src/main.rs +++ b/mipidsi/examples/parallel_ili9341_rp_pico/src/main.rs @@ -28,7 +28,7 @@ use embedded_graphics::{ }; // Provides the parallel port and display interface builders -use display_interface_parallel_gpio::{Generic8BitBus, PGPIO8BitInterface}; +use mipidsi::interface::{Generic8BitBus, ParallelInterface}; // Provides the Display builder use mipidsi::Builder; @@ -96,7 +96,7 @@ fn main() -> ! { )); // Define the display interface from a generic 8 bit bus, a Data/Command select pin and a write enable pin - let di = PGPIO8BitInterface::new(bus, dc, wr); + let di = ParallelInterface::new(bus, dc, wr); // Define the display from the display bus, set the color order as Bgr and initialize it with // the delay struct and the reset pin diff --git a/mipidsi/examples/spi-ili9486-esp32-c3/Cargo.toml b/mipidsi/examples/spi-ili9486-esp32-c3/Cargo.toml index 9e43190..c37a035 100644 --- a/mipidsi/examples/spi-ili9486-esp32-c3/Cargo.toml +++ b/mipidsi/examples/spi-ili9486-esp32-c3/Cargo.toml @@ -6,10 +6,14 @@ edition = "2021" [dependencies] mipidsi = { path = "../../" } hal = { package = "esp-hal", version = "0.21", features = ["esp32c3"] } -esp-backtrace = { version = "0.14", features = ["esp32c3", "panic-handler", "exception-handler", "println"] } +esp-backtrace = { version = "0.14", features = [ + "esp32c3", + "panic-handler", + "exception-handler", + "println", +] } esp-println = { version = "0.12", features = ["esp32c3"] } embedded-graphics = "0.8.0" -display-interface-spi = "0.5.0" fugit = "0.3.7" embedded-hal-bus = "0.2.0" diff --git a/mipidsi/examples/spi-ili9486-esp32-c3/src/main.rs b/mipidsi/examples/spi-ili9486-esp32-c3/src/main.rs index 2958826..41bfb1f 100644 --- a/mipidsi/examples/spi-ili9486-esp32-c3/src/main.rs +++ b/mipidsi/examples/spi-ili9486-esp32-c3/src/main.rs @@ -21,7 +21,7 @@ use embedded_graphics::{ }; // Provides the parallel port and display interface builders -use display_interface_spi::SPIInterface; +use mipidsi::interface::SpiInterface; // Provides the Display builder use mipidsi::{models::ILI9486Rgb565, Builder}; @@ -71,8 +71,10 @@ fn main() -> ! { let cs_output = Output::new(cs, Level::High); let spi_device = ExclusiveDevice::new_no_delay(spi, cs_output).unwrap(); + let mut buffer = [0_u8; 512]; + // Define the display interface with no chip select - let di = SPIInterface::new(spi_device, dc); + let di = SpiInterface::new(spi_device, dc, &mut buffer); // Define the display from the display interface and initialize it let mut display = Builder::new(ILI9486Rgb565, di) diff --git a/mipidsi/examples/spi-st7789-rpi-zero-w/Cargo.toml b/mipidsi/examples/spi-st7789-rpi-zero-w/Cargo.toml index b16d44b..f230dbe 100644 --- a/mipidsi/examples/spi-st7789-rpi-zero-w/Cargo.toml +++ b/mipidsi/examples/spi-st7789-rpi-zero-w/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] mipidsi = { path = "../../" } -display-interface-spi = "0.5.0" embedded-graphics = "0.8.0" rppal = { version = "0.17.1", features = ["hal"] } embedded-hal-bus = "0.2.0" diff --git a/mipidsi/examples/spi-st7789-rpi-zero-w/src/main.rs b/mipidsi/examples/spi-st7789-rpi-zero-w/src/main.rs index c0e8460..b59344b 100644 --- a/mipidsi/examples/spi-st7789-rpi-zero-w/src/main.rs +++ b/mipidsi/examples/spi-st7789-rpi-zero-w/src/main.rs @@ -16,7 +16,6 @@ Buttons: Read the README.md for more information. */ -use display_interface_spi::SPIInterface; use embedded_graphics::{ mono_font::{ascii::FONT_10X20, MonoTextStyle}, pixelcolor::Rgb565, @@ -24,6 +23,7 @@ use embedded_graphics::{ text::Text, }; use embedded_hal_bus::spi::ExclusiveDevice; +use mipidsi::interface::SpiInterface; use mipidsi::{models::ST7789, options::ColorInversion, Builder}; use rppal::gpio::Gpio; use rppal::hal::Delay; @@ -68,7 +68,8 @@ fn main() -> ExitCode { // SPI Display let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss1, 60_000_000_u32, Mode::Mode0).unwrap(); let spi_device = ExclusiveDevice::new_no_delay(spi, NoCs).unwrap(); - let di = SPIInterface::new(spi_device, dc); + let mut buffer = [0_u8; 512]; + let di = SpiInterface::new(spi_device, dc, &mut buffer); let mut delay = Delay::new(); let mut display = Builder::new(ST7789, di) .display_size(W as u16, H as u16) From 22dbe21d02352e339e6873afbcc381210b4c0f4b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 1 Dec 2024 12:48:39 -0600 Subject: [PATCH 19/36] Update changelog and migration --- docs/MIGRATION.md | 40 ++++++++++++++++++++++++++++++++++++++++ mipidsi/CHANGELOG.md | 4 ++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 334007b..fb6a94e 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -1,5 +1,45 @@ # Migration guide for `mipidsi` crate +## v0.8 -> 0.9 + +### Users + +* The `display_interface` dependancy has been removed in favor of our own traits and implementations. This gives significantly better performance (#149) + + * Replace `display_interface_spi::SPIInterface` with `mipidsi::interface::SpiInterface`. Note that it requires a small/medium sized `&mut [u8]` buffer. 512 bytes works well, your milage may vary. + ```rust + // before + use display_interface_spi::SPIInterface; + + let di = SPIInterface::new(spi_device, dc); + + // after + use mipidsi::interface::SpiInterface; + + let mut buffer = [0_u8; 512]; + let di = SpiInterface::new(spi_device, dc, &mut buffer); + ``` + + * Replace `display_interface_parallel_gpio::PGPIO{8,16}BitInterface` with `mipidsi::interface::ParallelInterface` and use `Generic{8,16}BitBus` from `mipidsi::interface` + ```rust + // before + use display_interface_parallel_gpio::Generic8BitBus; + use display_interface_parallel_gpio::PGPIO8BitInterface; + + let bus = Generic8BitBus::new(pins); + let di = PGPIO8BitInterface::new(bus, dc, wr); + + // after + use mipidsi::interface::Generic8BitBus; + use mipidsi::interface::ParallelInterface; + + let bus = Generic8BitBus::new(pins); + let di = ParallelInterface::new(bus, dc, wr); + ``` + + * If you have a custom impl of the `display_interface_parallel_gpio::OutputBus` trait, replace it with `mipidsi::interface::OutputBus`. This trait is identical except that it uses an associated error type instead of being hardcoded to `display_interface::DisplayError` + + ## v0.7 -> 0.8 ### Users diff --git a/mipidsi/CHANGELOG.md b/mipidsi/CHANGELOG.md index 1d41466..f20fe66 100644 --- a/mipidsi/CHANGELOG.md +++ b/mipidsi/CHANGELOG.md @@ -7,9 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Added +### Changed -- added `Display::set_pixels_from_buffer` to allow high performance display writes +- replaced `display_interface` with our own traits and implementations for significanly better performance, see the migration page for details (#149) ## [v0.8.0] - 2024-05-24 From 4c988620f31221efbf4a6e532df000357943d221 Mon Sep 17 00:00:00 2001 From: GrantM11235 Date: Wed, 4 Dec 2024 14:02:13 -0600 Subject: [PATCH 20/36] Update docs/MIGRATION.md Co-authored-by: Gram --- docs/MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index fb6a94e..83140d1 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -4,7 +4,7 @@ ### Users -* The `display_interface` dependancy has been removed in favor of our own traits and implementations. This gives significantly better performance (#149) +* The `display_interface` dependency has been removed in favor of our own traits and implementations. This gives significantly better performance (#149) * Replace `display_interface_spi::SPIInterface` with `mipidsi::interface::SpiInterface`. Note that it requires a small/medium sized `&mut [u8]` buffer. 512 bytes works well, your milage may vary. ```rust From 6fcdc1a4b0890e16eb10d965cbc6fa918674d385 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 4 Dec 2024 19:35:57 -0600 Subject: [PATCH 21/36] Merge CommandInterface and PixelInterface --- docs/MIGRATION.md | 2 +- mipidsi/CHANGELOG.md | 2 +- mipidsi/src/batch.rs | 6 +-- mipidsi/src/builder.rs | 8 ++-- mipidsi/src/dcs.rs | 8 ++-- mipidsi/src/graphics.rs | 6 +-- mipidsi/src/interface/mod.rs | 63 ++++++++++++++----------------- mipidsi/src/interface/parallel.rs | 23 ++++------- mipidsi/src/interface/spi.rs | 17 ++++----- mipidsi/src/lib.rs | 25 +++++------- mipidsi/src/models.rs | 4 +- mipidsi/src/models/gc9a01.rs | 4 +- mipidsi/src/models/ili9341.rs | 6 +-- mipidsi/src/models/ili9342c.rs | 6 +-- mipidsi/src/models/ili934x.rs | 4 +- mipidsi/src/models/ili9486.rs | 8 ++-- mipidsi/src/models/st7735s.rs | 4 +- mipidsi/src/models/st7789.rs | 4 +- mipidsi/src/models/st7796.rs | 6 +-- mipidsi/tests/external.rs | 4 +- 20 files changed, 92 insertions(+), 118 deletions(-) diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 83140d1..6d8c51b 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -4,7 +4,7 @@ ### Users -* The `display_interface` dependency has been removed in favor of our own traits and implementations. This gives significantly better performance (#149) +* The `display_interface` dependency has been removed in favor of our own trait and implementations. This gives significantly better performance (#149) * Replace `display_interface_spi::SPIInterface` with `mipidsi::interface::SpiInterface`. Note that it requires a small/medium sized `&mut [u8]` buffer. 512 bytes works well, your milage may vary. ```rust diff --git a/mipidsi/CHANGELOG.md b/mipidsi/CHANGELOG.md index f20fe66..2d4fa5b 100644 --- a/mipidsi/CHANGELOG.md +++ b/mipidsi/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- replaced `display_interface` with our own traits and implementations for significanly better performance, see the migration page for details (#149) +- replaced `display_interface` with our own trait and implementations for significanly better performance, see the migration page for details (#149) ## [v0.8.0] - 2024-05-24 diff --git a/mipidsi/src/batch.rs b/mipidsi/src/batch.rs index d3c71cc..e3560c1 100644 --- a/mipidsi/src/batch.rs +++ b/mipidsi/src/batch.rs @@ -2,7 +2,7 @@ //! Batch the pixels to be rendered into Pixel Rows and Pixel Blocks (contiguous Pixel Rows). //! This enables the pixels to be rendered efficiently as Pixel Blocks, which may be transmitted in a single Non-Blocking SPI request. use crate::{ - interface::{PixelFormat, PixelInterface}, + interface::{Interface, PixelFormat}, models::Model, Display, }; @@ -11,7 +11,7 @@ use embedded_hal::digital::OutputPin; pub trait DrawBatch where - DI: PixelInterface, + DI: Interface, M: Model, M::ColorFormat: PixelFormat, I: IntoIterator>, @@ -21,7 +21,7 @@ where impl DrawBatch for Display where - DI: PixelInterface, + DI: Interface, M: Model, M::ColorFormat: PixelFormat, I: IntoIterator>, diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index e9e2071..6c921d2 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -3,7 +3,7 @@ use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; -use crate::interface::{PixelFormat, PixelInterface}; +use crate::interface::{Interface, PixelFormat}; use crate::{dcs::InterfaceExt, error::InitError, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -28,7 +28,7 @@ use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, Refr /// ``` pub struct Builder where - DI: PixelInterface, + DI: Interface, MODEL: Model, MODEL::ColorFormat: PixelFormat, { @@ -40,7 +40,7 @@ where impl Builder where - DI: PixelInterface, + DI: Interface, MODEL: Model, MODEL::ColorFormat: PixelFormat, { @@ -60,7 +60,7 @@ where impl Builder where - DI: PixelInterface, + DI: Interface, MODEL: Model, MODEL::ColorFormat: PixelFormat, RST: OutputPin, diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index 513e2f7..669edd9 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -1,6 +1,6 @@ //! MIPI DCS commands. -use crate::interface::CommandInterface; +use crate::interface::Interface; #[macro_use] mod macros; @@ -33,7 +33,7 @@ pub trait DcsCommand { fn fill_params_buf(&self, buffer: &mut [u8]) -> usize; } -/// Wrapper around [`CommandInterface`] with support for writing DCS commands. +/// Wrapper around [`Interface`] with support for writing DCS commands. /// /// Commands which are part of the manufacturer independent user command set can be sent to the /// display by using the [`write_command`](Self::write_command) method with one of the command types @@ -41,7 +41,7 @@ pub trait DcsCommand { /// /// All other commands, which do not have an associated type in this module, can be sent using /// the [`write_raw`](Self::write_raw) method. -pub trait InterfaceExt: CommandInterface { +pub trait InterfaceExt: Interface { /// Sends a DCS command to the display interface. fn write_command(&mut self, command: impl DcsCommand) -> Result<(), Self::Error> { let mut param_bytes: [u8; 16] = [0; 16]; @@ -64,7 +64,7 @@ pub trait InterfaceExt: CommandInterface { } } -impl InterfaceExt for T {} +impl InterfaceExt for T {} // DCS commands that don't use any parameters diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 8a5e694..730f5ad 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -8,13 +8,13 @@ use embedded_graphics_core::{ use embedded_hal::digital::OutputPin; use crate::dcs::InterfaceExt; -use crate::{dcs::BitsPerPixel, interface::PixelInterface}; +use crate::{dcs::BitsPerPixel, interface::Interface}; use crate::{dcs::WriteMemoryStart, models::Model}; use crate::{interface::PixelFormat, Display}; impl DrawTarget for Display where - DI: PixelInterface, + DI: Interface, M: Model, M::ColorFormat: PixelFormat, RST: OutputPin, @@ -120,7 +120,7 @@ where impl OriginDimensions for Display where - DI: PixelInterface, + DI: Interface, MODEL: Model, MODEL::ColorFormat: PixelFormat, RST: OutputPin, diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 0cd6719..ae3d126 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -7,33 +7,28 @@ pub use spi::*; mod parallel; pub use parallel::*; -/// Command interface -pub trait CommandInterface { +/// Command and pixel interface +pub trait Interface { + /// The native width of the interface + /// + /// In most cases this will be u8, except for larger parallel interfaces such as + /// 16 bit (currently supported) + /// or 9 or 18 bit (currently unsupported) + type PixelWord: Copy; + /// Error type type Error: core::fmt::Debug; /// Send a command with optional parameters /// - /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error>; - /// Sends any remaining buffered data - fn flush(&mut self) -> Result<(), Self::Error>; -} - -/// Pixel interface -pub trait PixelInterface: CommandInterface { - /// The native width of the interface - /// - /// In most cases this will be u8, except for larger parallel interfaces such as - /// 16 bit (currently supported) - /// or 9 or 18 bit (currently unsupported) - type PixelWord: Copy; /// Send a sequence of pixels /// /// `WriteMemoryStart` must be sent before calling this function /// - /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_pixels( &mut self, pixels: impl IntoIterator, @@ -43,29 +38,25 @@ pub trait PixelInterface: CommandInterface { /// /// `WriteMemoryStart` must be sent before calling this function /// - /// [`CommandInterface::flush`] must be called to ensure the data is actually sent + /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_repeated_pixel( &mut self, pixel: [Self::PixelWord; N], count: u32, ) -> Result<(), Self::Error>; + + /// Sends any remaining buffered data + fn flush(&mut self) -> Result<(), Self::Error>; } -impl CommandInterface for &mut T { +impl Interface for &mut T { + type PixelWord = T::PixelWord; type Error = T::Error; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { T::send_command(self, command, args) } - fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self) - } -} - -impl PixelInterface for &mut T { - type PixelWord = T::PixelWord; - fn send_pixels( &mut self, pixels: impl IntoIterator, @@ -80,6 +71,10 @@ impl PixelInterface for &mut T { ) -> Result<(), Self::Error> { T::send_repeated_pixel(self, pixel, count) } + + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } } fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { @@ -102,13 +97,13 @@ pub trait PixelFormat { // but that doesn't work yet #[doc(hidden)] - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error>; #[doc(hidden)] - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -116,14 +111,14 @@ pub trait PixelFormat { } impl PixelFormat for Rgb565 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb565_to_bytes)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -133,14 +128,14 @@ impl PixelFormat for Rgb565 { } impl PixelFormat for Rgb666 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb666_to_bytes)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -150,14 +145,14 @@ impl PixelFormat for Rgb666 { } impl PixelFormat for Rgb565 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb565_to_u16)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 502a36b..e5afbce 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -1,6 +1,6 @@ use embedded_hal::digital::OutputPin; -use super::{CommandInterface, PixelInterface}; +use super::Interface; /// This trait represents the data pins of a parallel bus. /// @@ -189,13 +189,14 @@ where } } -impl CommandInterface for ParallelInterface +impl Interface for ParallelInterface where BUS: OutputBus, BUS::Word: From + Eq, DC: OutputPin, WR: OutputPin, { + type PixelWord = BUS::Word; type Error = ParallelError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -210,20 +211,6 @@ where Ok(()) } - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl PixelInterface for ParallelInterface -where - BUS: OutputBus, - BUS::Word: From + Eq, - DC: OutputPin, - WR: OutputPin, -{ - type PixelWord = BUS::Word; - fn send_pixels( &mut self, pixels: impl IntoIterator, @@ -256,6 +243,10 @@ where self.send_pixels((0..count).map(|_| pixel)) } } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } } fn is_same(array: [T; N]) -> Option { diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 439fc85..0b73f64 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -1,6 +1,6 @@ use embedded_hal::{digital::OutputPin, spi::SpiDevice}; -use super::{CommandInterface, PixelInterface}; +use super::Interface; /// Spi interface error #[derive(Clone, Copy, Debug)] @@ -25,7 +25,8 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { } } -impl CommandInterface for SpiInterface<'_, SPI, DC> { +impl Interface for SpiInterface<'_, SPI, DC> { + type PixelWord = u8; type Error = SpiError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -36,14 +37,6 @@ impl CommandInterface for SpiInterface<'_, SPI, D Ok(()) } - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl PixelInterface for SpiInterface<'_, SPI, DC> { - type PixelWord = u8; - fn send_pixels( &mut self, pixels: impl IntoIterator, @@ -96,4 +89,8 @@ impl PixelInterface for SpiInterface<'_, SPI, DC> } Ok(()) } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } } diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index e49a4d8..0672e7c 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -133,7 +133,7 @@ mod batch; /// pub struct Display where - DI: interface::PixelInterface, + DI: interface::Interface, MODEL: Model, MODEL::ColorFormat: PixelFormat, RST: OutputPin, @@ -154,7 +154,7 @@ where impl Display where - DI: interface::PixelInterface, + DI: interface::Interface, M: Model, M::ColorFormat: PixelFormat, RST: OutputPin, @@ -391,11 +391,7 @@ pub mod _mock { use embedded_hal::{delay::DelayNs, digital, spi}; - use crate::{ - interface::{CommandInterface, PixelInterface}, - models::ILI9341Rgb565, - Builder, Display, NoResetPin, - }; + use crate::{interface::Interface, models::ILI9341Rgb565, Builder, Display, NoResetPin}; pub fn new_mock_display() -> Display { Builder::new(ILI9341Rgb565, MockDisplayInterface) @@ -442,21 +438,14 @@ pub mod _mock { pub struct MockDisplayInterface; - impl CommandInterface for MockDisplayInterface { + impl Interface for MockDisplayInterface { + type PixelWord = u8; type Error = Infallible; fn send_command(&mut self, _command: u8, _args: &[u8]) -> Result<(), Self::Error> { Ok(()) } - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl PixelInterface for MockDisplayInterface { - type PixelWord = u8; - fn send_pixels( &mut self, _pixels: impl IntoIterator, @@ -471,5 +460,9 @@ pub mod _mock { ) -> Result<(), Self::Error> { Ok(()) } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } } } diff --git a/mipidsi/src/models.rs b/mipidsi/src/models.rs index 0c0add6..154b2d1 100644 --- a/mipidsi/src/models.rs +++ b/mipidsi/src/models.rs @@ -1,6 +1,6 @@ //! Display models. -use crate::{dcs::SetAddressMode, interface::CommandInterface, options::ModelOptions}; +use crate::{dcs::SetAddressMode, interface::Interface, options::ModelOptions}; use embedded_graphics_core::prelude::RgbColor; use embedded_hal::delay::DelayNs; @@ -40,5 +40,5 @@ pub trait Model { ) -> Result where DELAY: DelayNs, - DI: CommandInterface; + DI: Interface; } diff --git a/mipidsi/src/models/gc9a01.rs b/mipidsi/src/models/gc9a01.rs index 19590a3..3441c4e 100644 --- a/mipidsi/src/models/gc9a01.rs +++ b/mipidsi/src/models/gc9a01.rs @@ -6,7 +6,7 @@ use crate::{ BitsPerPixel, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, options::ModelOptions, }; @@ -27,7 +27,7 @@ impl Model for GC9A01 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); diff --git a/mipidsi/src/models/ili9341.rs b/mipidsi/src/models/ili9341.rs index 8199433..c9deee1 100644 --- a/mipidsi/src/models/ili9341.rs +++ b/mipidsi/src/models/ili9341.rs @@ -3,7 +3,7 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{BitsPerPixel, PixelFormat, SetAddressMode}, - interface::CommandInterface, + interface::Interface, models::{ili934x, Model}, options::ModelOptions, }; @@ -26,7 +26,7 @@ impl Model for ILI9341Rgb565 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(di, delay, options, pf).map_err(Into::into) @@ -45,7 +45,7 @@ impl Model for ILI9341Rgb666 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(di, delay, options, pf).map_err(Into::into) diff --git a/mipidsi/src/models/ili9342c.rs b/mipidsi/src/models/ili9342c.rs index 29be67a..8268ae4 100644 --- a/mipidsi/src/models/ili9342c.rs +++ b/mipidsi/src/models/ili9342c.rs @@ -3,7 +3,7 @@ use embedded_hal::delay::DelayNs; use crate::{ dcs::{BitsPerPixel, PixelFormat, SetAddressMode}, - interface::CommandInterface, + interface::Interface, models::{ili934x, Model}, options::ModelOptions, }; @@ -26,7 +26,7 @@ impl Model for ILI9342CRgb565 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(di, delay, options, pf).map_err(Into::into) @@ -45,7 +45,7 @@ impl Model for ILI9342CRgb666 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let pf = PixelFormat::with_all(BitsPerPixel::from_rgb_color::()); ili934x::init_common(di, delay, options, pf).map_err(Into::into) diff --git a/mipidsi/src/models/ili934x.rs b/mipidsi/src/models/ili934x.rs index 17df36c..ef67aa7 100644 --- a/mipidsi/src/models/ili934x.rs +++ b/mipidsi/src/models/ili934x.rs @@ -5,7 +5,7 @@ use crate::{ EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, options::ModelOptions, }; @@ -18,7 +18,7 @@ pub fn init_common( ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); diff --git a/mipidsi/src/models/ili9486.rs b/mipidsi/src/models/ili9486.rs index 471184e..42f178c 100644 --- a/mipidsi/src/models/ili9486.rs +++ b/mipidsi/src/models/ili9486.rs @@ -6,7 +6,7 @@ use crate::{ BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, options::ModelOptions, }; @@ -30,7 +30,7 @@ impl Model for ILI9486Rgb565 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { delay.delay_us(120_000); @@ -51,7 +51,7 @@ impl Model for ILI9486Rgb666 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { delay.delay_us(120_000); @@ -69,7 +69,7 @@ fn init_common( ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); di.write_command(ExitSleepMode)?; // turn off sleep diff --git a/mipidsi/src/models/st7735s.rs b/mipidsi/src/models/st7735s.rs index 576f0f4..2baebdc 100644 --- a/mipidsi/src/models/st7735s.rs +++ b/mipidsi/src/models/st7735s.rs @@ -6,7 +6,7 @@ use crate::{ BitsPerPixel, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, models::Model, options::ModelOptions, }; @@ -26,7 +26,7 @@ impl Model for ST7735s { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); diff --git a/mipidsi/src/models/st7789.rs b/mipidsi/src/models/st7789.rs index ff59b67..e779576 100644 --- a/mipidsi/src/models/st7789.rs +++ b/mipidsi/src/models/st7789.rs @@ -6,7 +6,7 @@ use crate::{ BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, models::Model, options::ModelOptions, }; @@ -28,7 +28,7 @@ impl Model for ST7789 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); diff --git a/mipidsi/src/models/st7796.rs b/mipidsi/src/models/st7796.rs index 2fd4f47..d415bee 100644 --- a/mipidsi/src/models/st7796.rs +++ b/mipidsi/src/models/st7796.rs @@ -1,9 +1,7 @@ use embedded_graphics_core::pixelcolor::Rgb565; use embedded_hal::delay::DelayNs; -use crate::{ - dcs::SetAddressMode, interface::CommandInterface, models::Model, options::ModelOptions, -}; +use crate::{dcs::SetAddressMode, interface::Interface, models::Model, options::ModelOptions}; /// ST7796 display in Rgb565 color mode. /// @@ -22,7 +20,7 @@ impl Model for ST7796 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { super::ST7789.init(di, delay, options) } diff --git a/mipidsi/tests/external.rs b/mipidsi/tests/external.rs index 6795fc4..27a6161 100644 --- a/mipidsi/tests/external.rs +++ b/mipidsi/tests/external.rs @@ -5,7 +5,7 @@ use mipidsi::{ BitsPerPixel, EnterNormalMode, ExitSleepMode, InterfaceExt, PixelFormat, SetAddressMode, SetDisplayOn, SetInvertMode, SetPixelFormat, }, - interface::CommandInterface, + interface::Interface, models::Model, options::ModelOptions, }; @@ -26,7 +26,7 @@ impl Model for ExternalST7789 { ) -> Result where DELAY: DelayNs, - DI: CommandInterface, + DI: Interface, { let madctl = SetAddressMode::from(options); From 1663261c7f4809a7a035fc9f7893d6a4df49987b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 4 Dec 2024 19:48:00 -0600 Subject: [PATCH 22/36] Remove display-interface from docs --- README.md | 2 +- mipidsi/README.md | 4 ++-- mipidsi/src/lib.rs | 9 ++++----- mipidsi/src/models/st7789.rs | 2 -- mipidsi/src/models/st7796.rs | 2 -- 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8efc2d4..b8d2bdd 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It consists of *NOTES*: -* The name of these crates is a bit unfortunate as the drivers work with displays that use the MIPI Display Command Set via any transport supported by [display_interface](https://crates.io/crates/display-interface) but MIPI Display Serial Interface is NOT supported at this time. +* The name of these crates is a bit unfortunate as the drivers work with displays that use the MIPI Display Command Set but MIPI Display Serial Interface is NOT supported at this time. ## License diff --git a/mipidsi/README.md b/mipidsi/README.md index e256d13..d2811f7 100644 --- a/mipidsi/README.md +++ b/mipidsi/README.md @@ -9,13 +9,13 @@ This crate provides a generic display driver to connect to TFT displays that implement the [MIPI Display Command Set](https://www.mipi.org/specifications/display-command-set). -Uses [display_interface](https://crates.io/crates/display-interface) to talk to the hardware via transports (currently SPI, I2C and Parallel GPIO). +Uses `interface::Interface` to talk to the hardware via transports (currently SPI, I2C and Parallel GPIO). An optional batching of draws is supported via the `batch` feature (default on) *NOTES*: -* The name of this crate is a bit unfortunate as this driver works with displays that use the MIPI Display Command Set via any transport supported by [display_interface](https://crates.io/crates/display-interface) but MIPI Display Serial Interface is NOT supported at this time. +* The name of this crate is a bit unfortunate as this driver works with displays that use the MIPI Display Command Set but MIPI Display Serial Interface is NOT supported at this time. ## License diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 0672e7c..a3db42e 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -6,7 +6,7 @@ //! This crate provides a generic display driver to connect to TFT displays //! that implement the [MIPI Display Command Set](https://www.mipi.org/specifications/display-command-set). //! -//! Uses [display_interface](https://crates.io/crates/display-interface) to talk to the hardware via transports. +//! Uses [interface::Interface] to talk to the hardware via transports. //! //! An optional batching of draws is supported via the `batch` feature (default on) //! @@ -92,9 +92,8 @@ //! display.clear(Rgb666::RED).unwrap(); //! ``` //! Use the appropriate display interface crate for your needs: -//! - [`display-interface-spi`](https://docs.rs/display-interface-spi/) -//! - [`display-interface-parallel-gpio`](https://docs.rs/display-interface-parallel-gpio) -//! - [`display-interface-i2c`](https://docs.rs/display-interface-i2c/) +//! - [`interface::SpiInterface`] +//! - [`interface::ParallelInterface`] //! //! ## Troubleshooting //! See [document](https://github.com/almindor/mipidsi/blob/master/docs/TROUBLESHOOTING.md) @@ -382,7 +381,7 @@ where } } -/// Mock implementations of embedded-hal and display-interface traits. +/// Mock implementations of embedded-hal and interface traits. /// /// Do not use types in this module outside of doc tests. #[doc(hidden)] diff --git a/mipidsi/src/models/st7789.rs b/mipidsi/src/models/st7789.rs index e779576..0c47a2f 100644 --- a/mipidsi/src/models/st7789.rs +++ b/mipidsi/src/models/st7789.rs @@ -12,8 +12,6 @@ use crate::{ }; /// ST7789 display in Rgb565 color mode. -/// -/// Interfaces implemented by the [display-interface](https://crates.io/crates/display-interface) are supported. pub struct ST7789; impl Model for ST7789 { diff --git a/mipidsi/src/models/st7796.rs b/mipidsi/src/models/st7796.rs index d415bee..4652d2c 100644 --- a/mipidsi/src/models/st7796.rs +++ b/mipidsi/src/models/st7796.rs @@ -4,8 +4,6 @@ use embedded_hal::delay::DelayNs; use crate::{dcs::SetAddressMode, interface::Interface, models::Model, options::ModelOptions}; /// ST7796 display in Rgb565 color mode. -/// -/// Interfaces implemented by the [display-interface](https://crates.io/crates/display-interface) are supported. pub struct ST7796; impl Model for ST7796 { From a682af05baff3b66360f944c3b5ead7ee78bc1c3 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Dec 2024 11:32:10 -0600 Subject: [PATCH 23/36] Move InitError and rename variants --- mipidsi/src/builder.rs | 19 ++++++++++++++----- mipidsi/src/error.rs | 10 ---------- mipidsi/src/lib.rs | 2 -- 3 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 mipidsi/src/error.rs diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index 6c921d2..6b983a7 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -4,7 +4,7 @@ use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; use crate::interface::{Interface, PixelFormat}; -use crate::{dcs::InterfaceExt, error::InitError, models::Model, Display}; +use crate::{dcs::InterfaceExt, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -162,20 +162,20 @@ where match self.rst { Some(ref mut rst) => { - rst.set_low().map_err(InitError::Pin)?; + rst.set_low().map_err(InitError::ResetPin)?; delay_source.delay_us(10); - rst.set_high().map_err(InitError::Pin)?; + rst.set_high().map_err(InitError::ResetPin)?; } None => self .di .write_command(crate::dcs::SoftReset) - .map_err(InitError::DisplayError)?, + .map_err(InitError::Interface)?, } let madctl = self .model .init(&mut self.di, delay_source, &self.options) - .map_err(InitError::DisplayError)?; + .map_err(InitError::Interface)?; let display = Display { di: self.di, @@ -190,6 +190,15 @@ where } } +/// Error returned by [`Builder::init`]. +#[derive(Debug)] +pub enum InitError { + /// Error caused by the display interface. + Interface(DI), + /// Error caused by the reset pin's [`OutputPin`](embedded_hal::digital::OutputPin) implementation. + ResetPin(P), +} + /// Marker type for no reset pin. pub enum NoResetPin {} diff --git a/mipidsi/src/error.rs b/mipidsi/src/error.rs deleted file mode 100644 index 143b20b..0000000 --- a/mipidsi/src/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Error module for [super::Display] - -/// Error returned by [`Builder::init`](crate::Builder). -#[derive(Debug)] -pub enum InitError { - /// Error caused by the display interface. - DisplayError(DI), - /// Error caused by the reset pin's [`OutputPin`](embedded_hal::digital::OutputPin) implementation. - Pin(P), -} diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index a3db42e..1b67449 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -102,8 +102,6 @@ use dcs::InterfaceExt; pub mod interface; -pub mod error; - use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; From 99c5acc2acaaf432000d663620839beaec5650dc Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Dec 2024 11:43:41 -0600 Subject: [PATCH 24/36] Clarify that "all pins" means all data bus pins --- mipidsi/src/interface/parallel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index e5afbce..721c6b7 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -152,7 +152,7 @@ pub enum ParallelError { /// [OutputBus] implementation as well as one /// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. /// -/// All pins are supposed to be high-active, high for the D/C pin meaning "data" and the +/// All pins in the data bus are supposed to be high-active. High for the D/C pin meaning "data" and the /// write-enable being pulled low before the setting of the bits and supposed to be sampled at a /// low to high edge. pub struct ParallelInterface { From 0a2c03c66affbae99b965cc1612da937bc5f4c34 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Dec 2024 11:48:13 -0600 Subject: [PATCH 25/36] It's an extension trait now --- mipidsi/src/dcs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index 669edd9..e5c1362 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -33,7 +33,7 @@ pub trait DcsCommand { fn fill_params_buf(&self, buffer: &mut [u8]) -> usize; } -/// Wrapper around [`Interface`] with support for writing DCS commands. +/// An extension trait for [`Interface`] with support for writing DCS commands. /// /// Commands which are part of the manufacturer independent user command set can be sent to the /// display by using the [`write_command`](Self::write_command) method with one of the command types From d8f2a0d41ce36cf131e1f7ccbbf967f440030892 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Dec 2024 17:03:23 -0600 Subject: [PATCH 26/36] Rename PixelWord to Word --- mipidsi/src/batch.rs | 4 ++-- mipidsi/src/builder.rs | 6 +++--- mipidsi/src/graphics.rs | 4 ++-- mipidsi/src/interface/mod.rs | 28 ++++++++++++++-------------- mipidsi/src/interface/parallel.rs | 6 +++--- mipidsi/src/interface/spi.rs | 6 +++--- mipidsi/src/lib.rs | 10 +++++----- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/mipidsi/src/batch.rs b/mipidsi/src/batch.rs index e3560c1..10efeef 100644 --- a/mipidsi/src/batch.rs +++ b/mipidsi/src/batch.rs @@ -13,7 +13,7 @@ pub trait DrawBatch where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: PixelFormat, I: IntoIterator>, { fn draw_batch(&mut self, item_pixels: I) -> Result<(), DI::Error>; @@ -23,7 +23,7 @@ impl DrawBatch for Display where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: PixelFormat, I: IntoIterator>, RST: OutputPin, { diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index 6b983a7..a6626a1 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -30,7 +30,7 @@ pub struct Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: PixelFormat, { di: DI, model: MODEL, @@ -42,7 +42,7 @@ impl Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: PixelFormat, { /// /// Constructs a new builder for given [Model]. @@ -62,7 +62,7 @@ impl Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { /// diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 730f5ad..8864ef2 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -16,7 +16,7 @@ impl DrawTarget for Display where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: PixelFormat, RST: OutputPin, { type Error = DI::Error; @@ -122,7 +122,7 @@ impl OriginDimensions for Display where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { fn size(&self) -> Size { diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index ae3d126..4928ba6 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -14,7 +14,7 @@ pub trait Interface { /// In most cases this will be u8, except for larger parallel interfaces such as /// 16 bit (currently supported) /// or 9 or 18 bit (currently unsupported) - type PixelWord: Copy; + type Word: Copy; /// Error type type Error: core::fmt::Debug; @@ -31,7 +31,7 @@ pub trait Interface { /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_pixels( &mut self, - pixels: impl IntoIterator, + pixels: impl IntoIterator, ) -> Result<(), Self::Error>; /// Send the same pixel value multiple times @@ -41,7 +41,7 @@ pub trait Interface { /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_repeated_pixel( &mut self, - pixel: [Self::PixelWord; N], + pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error>; @@ -50,7 +50,7 @@ pub trait Interface { } impl Interface for &mut T { - type PixelWord = T::PixelWord; + type Word = T::Word; type Error = T::Error; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -59,14 +59,14 @@ impl Interface for &mut T { fn send_pixels( &mut self, - pixels: impl IntoIterator, + pixels: impl IntoIterator, ) -> Result<(), Self::Error> { T::send_pixels(self, pixels) } fn send_repeated_pixel( &mut self, - pixel: [Self::PixelWord; N], + pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error> { T::send_repeated_pixel(self, pixel, count) @@ -97,13 +97,13 @@ pub trait PixelFormat { // but that doesn't work yet #[doc(hidden)] - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error>; #[doc(hidden)] - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -111,14 +111,14 @@ pub trait PixelFormat { } impl PixelFormat for Rgb565 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb565_to_bytes)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -128,14 +128,14 @@ impl PixelFormat for Rgb565 { } impl PixelFormat for Rgb666 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb666_to_bytes)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, @@ -145,14 +145,14 @@ impl PixelFormat for Rgb666 { } impl PixelFormat for Rgb565 { - fn send_pixels>( + fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, ) -> Result<(), DI::Error> { di.send_pixels(pixels.into_iter().map(rgb565_to_u16)) } - fn send_repeated_pixel>( + fn send_repeated_pixel>( di: &mut DI, pixel: Self, count: u32, diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 721c6b7..e922f21 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -196,7 +196,7 @@ where DC: OutputPin, WR: OutputPin, { - type PixelWord = BUS::Word; + type Word = BUS::Word; type Error = ParallelError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -213,7 +213,7 @@ where fn send_pixels( &mut self, - pixels: impl IntoIterator, + pixels: impl IntoIterator, ) -> Result<(), Self::Error> { for pixel in pixels { for word in pixel { @@ -225,7 +225,7 @@ where fn send_repeated_pixel( &mut self, - pixel: [Self::PixelWord; N], + pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error> { if count == 0 || N == 0 { diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 0b73f64..234a70c 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -26,7 +26,7 @@ impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { } impl Interface for SpiInterface<'_, SPI, DC> { - type PixelWord = u8; + type Word = u8; type Error = SpiError; fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { @@ -39,7 +39,7 @@ impl Interface for SpiInterface<'_, SPI, DC> { fn send_pixels( &mut self, - pixels: impl IntoIterator, + pixels: impl IntoIterator, ) -> Result<(), Self::Error> { let mut arrays = pixels.into_iter(); @@ -65,7 +65,7 @@ impl Interface for SpiInterface<'_, SPI, DC> { fn send_repeated_pixel( &mut self, - pixel: [Self::PixelWord; N], + pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error> { let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 1b67449..2e2c925 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -132,7 +132,7 @@ pub struct Display where DI: interface::Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: PixelFormat, RST: OutputPin, { // DCS provider @@ -153,7 +153,7 @@ impl Display where DI: interface::Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: PixelFormat, RST: OutputPin, { /// @@ -436,7 +436,7 @@ pub mod _mock { pub struct MockDisplayInterface; impl Interface for MockDisplayInterface { - type PixelWord = u8; + type Word = u8; type Error = Infallible; fn send_command(&mut self, _command: u8, _args: &[u8]) -> Result<(), Self::Error> { @@ -445,14 +445,14 @@ pub mod _mock { fn send_pixels( &mut self, - _pixels: impl IntoIterator, + _pixels: impl IntoIterator, ) -> Result<(), Self::Error> { Ok(()) } fn send_repeated_pixel( &mut self, - _pixel: [Self::PixelWord; N], + _pixel: [Self::Word; N], _count: u32, ) -> Result<(), Self::Error> { Ok(()) From b93d4fbb5a36018782d3761262f26d08a58bcaa3 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Dec 2024 17:41:09 -0600 Subject: [PATCH 27/36] Add FsmcAdapter to migration guide --- docs/MIGRATION.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 6d8c51b..3f9ea32 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -39,6 +39,55 @@ * If you have a custom impl of the `display_interface_parallel_gpio::OutputBus` trait, replace it with `mipidsi::interface::OutputBus`. This trait is identical except that it uses an associated error type instead of being hardcoded to `display_interface::DisplayError` + * If you are using `stm32f4xx-hal`'s fsmc, you can copy and paste this adapter: + ```rust + pub struct FsmcAdapter(pub Lcd); + + impl Interface for FsmcAdapter + where + S: SubBank, + WORD: Word + Copy + From, + { + type Word = WORD; + + type Error = Infallible; + + fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> { + self.0.write_command(command.into()); + for &arg in args { + self.0.write_data(arg.into()); + } + + Ok(()) + } + + fn send_pixels( + &mut self, + pixels: impl IntoIterator, + ) -> Result<(), Self::Error> { + for pixel in pixels { + for word in pixel { + self.0.write_data(word); + } + } + + Ok(()) + } + + fn send_repeated_pixel( + &mut self, + pixel: [Self::Word; N], + count: u32, + ) -> Result<(), Self::Error> { + self.send_pixels((0..count).map(|_| pixel)) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + } + ``` + ## v0.7 -> 0.8 From 5de6703fff049790f6a88a27859c8f9c424f8421 Mon Sep 17 00:00:00 2001 From: GrantM11235 Date: Sun, 8 Dec 2024 11:44:34 -0600 Subject: [PATCH 28/36] Update mipidsi/README.md Co-authored-by: Ralf Fuest --- mipidsi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mipidsi/README.md b/mipidsi/README.md index d2811f7..e21e42b 100644 --- a/mipidsi/README.md +++ b/mipidsi/README.md @@ -9,7 +9,7 @@ This crate provides a generic display driver to connect to TFT displays that implement the [MIPI Display Command Set](https://www.mipi.org/specifications/display-command-set). -Uses `interface::Interface` to talk to the hardware via transports (currently SPI, I2C and Parallel GPIO). +Uses `interface::Interface` to talk to the hardware via transports (currently SPI and Parallel GPIO). An optional batching of draws is supported via the `batch` feature (default on) From 3a5230ebbe909225149f4f87272fe63f8157717b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 8 Dec 2024 12:59:29 -0600 Subject: [PATCH 29/36] Move interfaces list --- mipidsi/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 2e2c925..74578ac 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -8,6 +8,10 @@ //! //! Uses [interface::Interface] to talk to the hardware via transports. //! +//! Use the appropriate display interface crate for your needs: +//! - [`interface::SpiInterface`] +//! - [`interface::ParallelInterface`] +//! //! An optional batching of draws is supported via the `batch` feature (default on) //! //! ### List of supported models @@ -91,9 +95,6 @@ //! // Clear the display to black //! display.clear(Rgb666::RED).unwrap(); //! ``` -//! Use the appropriate display interface crate for your needs: -//! - [`interface::SpiInterface`] -//! - [`interface::ParallelInterface`] //! //! ## Troubleshooting //! See [document](https://github.com/almindor/mipidsi/blob/master/docs/TROUBLESHOOTING.md) From 7ceede3a466f4484b51bd2f5bd8665dc85a97f73 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 8 Dec 2024 13:46:17 -0600 Subject: [PATCH 30/36] Explain SPI buffer --- mipidsi/src/interface/spi.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 234a70c..d1ea8cf 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -11,7 +11,11 @@ pub enum SpiError { Dc(DC), } -/// Spi interface +/// Spi interface, including a buffer +/// +/// The buffer is used to gather batches of pixel data to be sent over SPI. +/// Larger buffers will genererally be faster (with diminishing returns), at the expense of using more RAM. +/// The buffer should be at least big enough to hold a few pixels of data. pub struct SpiInterface<'a, SPI, DC> { spi: SPI, dc: DC, From 9a7e17cd3124bf9e533c2fdf2a987d1f3ed43adb Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 8 Dec 2024 14:11:20 -0600 Subject: [PATCH 31/36] Use AsMut for SPI buffer --- mipidsi/src/interface/spi.rs | 52 +++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index d1ea8cf..8bcdd9d 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -16,20 +16,44 @@ pub enum SpiError { /// The buffer is used to gather batches of pixel data to be sent over SPI. /// Larger buffers will genererally be faster (with diminishing returns), at the expense of using more RAM. /// The buffer should be at least big enough to hold a few pixels of data. -pub struct SpiInterface<'a, SPI, DC> { +/// +/// The buffer can be any type that implements [`AsMut<[u8]>`](AsMut) such as +/// - Mutable slices, `&mut [u8]` +/// - Owned arrays, `[u8; N]` +/// - Or even heap types like `Box<[u8]>` or `Vec` +/// +/// # Examples: +/// +/// Slice buffer +/// ```rust +/// # use mipidsi::interface::SpiInterface; +/// # let spi = mipidsi::_mock::MockSpi; +/// # let dc = mipidsi::_mock::MockOutputPin; +/// let mut buffer = [0_u8; 128]; +/// let iface = SpiInterface::new(spi, dc, &mut buffer); +/// ``` +/// +/// Array buffer +/// ```rust +/// # use mipidsi::interface::SpiInterface; +/// # let spi = mipidsi::_mock::MockSpi; +/// # let dc = mipidsi::_mock::MockOutputPin; +/// let iface = SpiInterface::new(spi, dc, [0_u8; 128]); +/// ``` +pub struct SpiInterface { spi: SPI, dc: DC, - buffer: &'a mut [u8], + buffer: B, } -impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { +impl> SpiInterface { /// Create new interface - pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { + pub fn new(spi: SPI, dc: DC, buffer: B) -> Self { Self { spi, dc, buffer } } } -impl Interface for SpiInterface<'_, SPI, DC> { +impl> Interface for SpiInterface { type Word = u8; type Error = SpiError; @@ -47,12 +71,14 @@ impl Interface for SpiInterface<'_, SPI, DC> { ) -> Result<(), Self::Error> { let mut arrays = pixels.into_iter(); - assert!(self.buffer.len() >= N); + let buffer = self.buffer.as_mut(); + + assert!(buffer.len() >= N); let mut done = false; while !done { let mut i = 0; - for chunk in self.buffer.chunks_exact_mut(N) { + for chunk in buffer.chunks_exact_mut(N) { if let Some(array) = arrays.next() { let chunk: &mut [u8; N] = chunk.try_into().unwrap(); *chunk = array; @@ -62,7 +88,7 @@ impl Interface for SpiInterface<'_, SPI, DC> { break; }; } - self.spi.write(&self.buffer[..i]).map_err(SpiError::Spi)?; + self.spi.write(&buffer[..i]).map_err(SpiError::Spi)?; } Ok(()) } @@ -72,9 +98,11 @@ impl Interface for SpiInterface<'_, SPI, DC> { pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error> { - let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); + let buffer = self.buffer.as_mut(); + + let fill_count = core::cmp::min(count, (buffer.len() / N) as u32); let filled_len = fill_count as usize * N; - for chunk in self.buffer[..(filled_len)].chunks_exact_mut(N) { + for chunk in buffer[..(filled_len)].chunks_exact_mut(N) { let chunk: &mut [u8; N] = chunk.try_into().unwrap(); *chunk = pixel; } @@ -82,13 +110,13 @@ impl Interface for SpiInterface<'_, SPI, DC> { let mut count = count; while count >= fill_count { self.spi - .write(&self.buffer[..filled_len]) + .write(&buffer[..filled_len]) .map_err(SpiError::Spi)?; count -= fill_count; } if count != 0 { self.spi - .write(&self.buffer[..(count as usize * pixel.len())]) + .write(&buffer[..(count as usize * pixel.len())]) .map_err(SpiError::Spi)?; } Ok(()) From f23956eae7fcbddee878455ae697785d9bc92087 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 13 Dec 2024 07:26:25 -0600 Subject: [PATCH 32/36] Revert "Use AsMut for SPI buffer" This reverts commit 9a7e17cd3124bf9e533c2fdf2a987d1f3ed43adb. --- mipidsi/src/interface/spi.rs | 52 +++++++++--------------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 8bcdd9d..d1ea8cf 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -16,44 +16,20 @@ pub enum SpiError { /// The buffer is used to gather batches of pixel data to be sent over SPI. /// Larger buffers will genererally be faster (with diminishing returns), at the expense of using more RAM. /// The buffer should be at least big enough to hold a few pixels of data. -/// -/// The buffer can be any type that implements [`AsMut<[u8]>`](AsMut) such as -/// - Mutable slices, `&mut [u8]` -/// - Owned arrays, `[u8; N]` -/// - Or even heap types like `Box<[u8]>` or `Vec` -/// -/// # Examples: -/// -/// Slice buffer -/// ```rust -/// # use mipidsi::interface::SpiInterface; -/// # let spi = mipidsi::_mock::MockSpi; -/// # let dc = mipidsi::_mock::MockOutputPin; -/// let mut buffer = [0_u8; 128]; -/// let iface = SpiInterface::new(spi, dc, &mut buffer); -/// ``` -/// -/// Array buffer -/// ```rust -/// # use mipidsi::interface::SpiInterface; -/// # let spi = mipidsi::_mock::MockSpi; -/// # let dc = mipidsi::_mock::MockOutputPin; -/// let iface = SpiInterface::new(spi, dc, [0_u8; 128]); -/// ``` -pub struct SpiInterface { +pub struct SpiInterface<'a, SPI, DC> { spi: SPI, dc: DC, - buffer: B, + buffer: &'a mut [u8], } -impl> SpiInterface { +impl<'a, SPI: SpiDevice, DC: OutputPin> SpiInterface<'a, SPI, DC> { /// Create new interface - pub fn new(spi: SPI, dc: DC, buffer: B) -> Self { + pub fn new(spi: SPI, dc: DC, buffer: &'a mut [u8]) -> Self { Self { spi, dc, buffer } } } -impl> Interface for SpiInterface { +impl Interface for SpiInterface<'_, SPI, DC> { type Word = u8; type Error = SpiError; @@ -71,14 +47,12 @@ impl> Interface for SpiInterface Result<(), Self::Error> { let mut arrays = pixels.into_iter(); - let buffer = self.buffer.as_mut(); - - assert!(buffer.len() >= N); + assert!(self.buffer.len() >= N); let mut done = false; while !done { let mut i = 0; - for chunk in buffer.chunks_exact_mut(N) { + for chunk in self.buffer.chunks_exact_mut(N) { if let Some(array) = arrays.next() { let chunk: &mut [u8; N] = chunk.try_into().unwrap(); *chunk = array; @@ -88,7 +62,7 @@ impl> Interface for SpiInterface> Interface for SpiInterface Result<(), Self::Error> { - let buffer = self.buffer.as_mut(); - - let fill_count = core::cmp::min(count, (buffer.len() / N) as u32); + let fill_count = core::cmp::min(count, (self.buffer.len() / N) as u32); let filled_len = fill_count as usize * N; - for chunk in buffer[..(filled_len)].chunks_exact_mut(N) { + for chunk in self.buffer[..(filled_len)].chunks_exact_mut(N) { let chunk: &mut [u8; N] = chunk.try_into().unwrap(); *chunk = pixel; } @@ -110,13 +82,13 @@ impl> Interface for SpiInterface= fill_count { self.spi - .write(&buffer[..filled_len]) + .write(&self.buffer[..filled_len]) .map_err(SpiError::Spi)?; count -= fill_count; } if count != 0 { self.spi - .write(&buffer[..(count as usize * pixel.len())]) + .write(&self.buffer[..(count as usize * pixel.len())]) .map_err(SpiError::Spi)?; } Ok(()) From 2c9f3fb93c946d9ca28220455edf2a0f87185f62 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 13 Dec 2024 07:43:38 -0600 Subject: [PATCH 33/36] Recommend static_cell --- mipidsi/src/interface/spi.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index d1ea8cf..9674efa 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -16,6 +16,9 @@ pub enum SpiError { /// The buffer is used to gather batches of pixel data to be sent over SPI. /// Larger buffers will genererally be faster (with diminishing returns), at the expense of using more RAM. /// The buffer should be at least big enough to hold a few pixels of data. +/// +/// You may want to use [static_cell](https://crates.io/crates/static_cell) +/// to obtain a `&'static mut [u8; N]` buffer. pub struct SpiInterface<'a, SPI, DC> { spi: SPI, dc: DC, From c036701bc51441f4bbd3eb118803f204a02c2c14 Mon Sep 17 00:00:00 2001 From: GrantM11235 Date: Sat, 14 Dec 2024 13:09:32 -0600 Subject: [PATCH 34/36] Apply suggestions from code review Co-authored-by: Ralf Fuest --- mipidsi/CHANGELOG.md | 3 +-- mipidsi/src/interface/parallel.rs | 4 ++-- mipidsi/src/lib.rs | 10 +++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/mipidsi/CHANGELOG.md b/mipidsi/CHANGELOG.md index 21a7ed4..4c320b5 100644 --- a/mipidsi/CHANGELOG.md +++ b/mipidsi/CHANGELOG.md @@ -13,8 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- replaced `display_interface` with our own trait and implementations for significanly better performance, see the migration page for details (#149) -aster +- replaced `display_interface` with our own trait and implementations for significantly better performance, see the [migration guide](https://github.com/almindor/mipidsi/blob/master/docs/MIGRATION.mdMIGRATION.md#v08---09) for details ## [v0.8.0] - 2024-05-24 diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index e922f21..2069e92 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -149,8 +149,8 @@ pub enum ParallelError { /// Parallel communication interface /// /// This interface implements a "8080" style write-only display interface using any -/// [OutputBus] implementation as well as one -/// `OutputPin` for the data/command selection and one `OutputPin` for the write-enable flag. +/// [`OutputBus`] implementation as well as one +/// [`OutputPin`] for the data/command selection and one [`OutputPin`] for the write-enable flag. /// /// All pins in the data bus are supposed to be high-active. High for the D/C pin meaning "data" and the /// write-enable being pulled low before the setting of the bits and supposed to be sampled at a diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 76a36b3..d621734 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -6,11 +6,11 @@ //! This crate provides a generic display driver to connect to TFT displays //! that implement the [MIPI Display Command Set](https://www.mipi.org/specifications/display-command-set). //! -//! Uses [interface::Interface] to talk to the hardware via transports. -//! -//! Use the appropriate display interface crate for your needs: -//! - [`interface::SpiInterface`] -//! - [`interface::ParallelInterface`] +//! Uses implementations of the [interface::Interface] trait to talk to the +//! hardware via different transports. Builtin support for these transports is +//! available: +//! - SPI ([`interface::SpiInterface`]) +//! - 8080 style parallel via GPIO ([`interface::ParallelInterface`]) //! //! An optional batching of draws is supported via the `batch` feature (default on) //! From 2b9efcca92ea1e1f1d6e488f8c461f82b2abede2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 17 Dec 2024 16:32:47 -0600 Subject: [PATCH 35/36] Remove flush --- docs/MIGRATION.md | 4 ---- mipidsi/src/dcs.rs | 3 +-- mipidsi/src/graphics.rs | 3 +-- mipidsi/src/interface/mod.rs | 13 ------------- mipidsi/src/interface/parallel.rs | 4 ---- mipidsi/src/interface/spi.rs | 4 ---- mipidsi/src/lib.rs | 8 +------- 7 files changed, 3 insertions(+), 36 deletions(-) diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 3f9ea32..d1cf311 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -81,10 +81,6 @@ ) -> Result<(), Self::Error> { self.send_pixels((0..count).map(|_| pixel)) } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } } ``` diff --git a/mipidsi/src/dcs.rs b/mipidsi/src/dcs.rs index e5c1362..10a35db 100644 --- a/mipidsi/src/dcs.rs +++ b/mipidsi/src/dcs.rs @@ -59,8 +59,7 @@ pub trait InterfaceExt: Interface { /// user command set. Use [`write_command`](Self::write_command) for commands in the user /// command set. fn write_raw(&mut self, instruction: u8, param_bytes: &[u8]) -> Result<(), Self::Error> { - self.send_command(instruction, param_bytes)?; - self.flush() + self.send_command(instruction, param_bytes) } } diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 8864ef2..0292d58 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -113,8 +113,7 @@ where self.set_address_window(sx, sy, ex, ey)?; self.di.write_command(WriteMemoryStart)?; - M::ColorFormat::send_repeated_pixel(&mut self.di, color, count)?; - self.di.flush() + M::ColorFormat::send_repeated_pixel(&mut self.di, color, count) } } diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 4928ba6..4e6a8bc 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -20,15 +20,11 @@ pub trait Interface { type Error: core::fmt::Debug; /// Send a command with optional parameters - /// - /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error>; /// Send a sequence of pixels /// /// `WriteMemoryStart` must be sent before calling this function - /// - /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_pixels( &mut self, pixels: impl IntoIterator, @@ -37,16 +33,11 @@ pub trait Interface { /// Send the same pixel value multiple times /// /// `WriteMemoryStart` must be sent before calling this function - /// - /// [`Interface::flush`] must be called to ensure the data is actually sent fn send_repeated_pixel( &mut self, pixel: [Self::Word; N], count: u32, ) -> Result<(), Self::Error>; - - /// Sends any remaining buffered data - fn flush(&mut self) -> Result<(), Self::Error>; } impl Interface for &mut T { @@ -71,10 +62,6 @@ impl Interface for &mut T { ) -> Result<(), Self::Error> { T::send_repeated_pixel(self, pixel, count) } - - fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self) - } } fn rgb565_to_bytes(pixel: Rgb565) -> [u8; 2] { diff --git a/mipidsi/src/interface/parallel.rs b/mipidsi/src/interface/parallel.rs index 2069e92..a06dff8 100644 --- a/mipidsi/src/interface/parallel.rs +++ b/mipidsi/src/interface/parallel.rs @@ -243,10 +243,6 @@ where self.send_pixels((0..count).map(|_| pixel)) } } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } } fn is_same(array: [T; N]) -> Option { diff --git a/mipidsi/src/interface/spi.rs b/mipidsi/src/interface/spi.rs index 9674efa..3ee4298 100644 --- a/mipidsi/src/interface/spi.rs +++ b/mipidsi/src/interface/spi.rs @@ -96,8 +96,4 @@ impl Interface for SpiInterface<'_, SPI, DC> { } Ok(()) } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } } diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index d621734..32efa6b 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -246,9 +246,7 @@ where self.di.write_command(dcs::WriteMemoryStart)?; - M::ColorFormat::send_pixels(&mut self.di, colors.into_iter())?; - - self.di.flush() + M::ColorFormat::send_pixels(&mut self.di, colors) } /// Sets the vertical scroll region. @@ -459,9 +457,5 @@ pub mod _mock { ) -> Result<(), Self::Error> { Ok(()) } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } } } From e7774c0b651a5e88f69aa9d4fb9fa6182e1f5f68 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 17 Dec 2024 16:39:01 -0600 Subject: [PATCH 36/36] Rename PixelFormat to InterfacePixelFormat --- mipidsi/src/batch.rs | 6 +++--- mipidsi/src/builder.rs | 8 ++++---- mipidsi/src/graphics.rs | 6 +++--- mipidsi/src/interface/mod.rs | 8 ++++---- mipidsi/src/lib.rs | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mipidsi/src/batch.rs b/mipidsi/src/batch.rs index 10efeef..40dd9ff 100644 --- a/mipidsi/src/batch.rs +++ b/mipidsi/src/batch.rs @@ -2,7 +2,7 @@ //! Batch the pixels to be rendered into Pixel Rows and Pixel Blocks (contiguous Pixel Rows). //! This enables the pixels to be rendered efficiently as Pixel Blocks, which may be transmitted in a single Non-Blocking SPI request. use crate::{ - interface::{Interface, PixelFormat}, + interface::{Interface, InterfacePixelFormat}, models::Model, Display, }; @@ -13,7 +13,7 @@ pub trait DrawBatch where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: InterfacePixelFormat, I: IntoIterator>, { fn draw_batch(&mut self, item_pixels: I) -> Result<(), DI::Error>; @@ -23,7 +23,7 @@ impl DrawBatch for Display where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: InterfacePixelFormat, I: IntoIterator>, RST: OutputPin, { diff --git a/mipidsi/src/builder.rs b/mipidsi/src/builder.rs index a6626a1..458b1fd 100644 --- a/mipidsi/src/builder.rs +++ b/mipidsi/src/builder.rs @@ -3,7 +3,7 @@ use embedded_hal::digital; use embedded_hal::{delay::DelayNs, digital::OutputPin}; -use crate::interface::{Interface, PixelFormat}; +use crate::interface::{Interface, InterfacePixelFormat}; use crate::{dcs::InterfaceExt, models::Model, Display}; use crate::options::{ColorInversion, ColorOrder, ModelOptions, Orientation, RefreshOrder}; @@ -30,7 +30,7 @@ pub struct Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: InterfacePixelFormat, { di: DI, model: MODEL, @@ -42,7 +42,7 @@ impl Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: InterfacePixelFormat, { /// /// Constructs a new builder for given [Model]. @@ -62,7 +62,7 @@ impl Builder where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: InterfacePixelFormat, RST: OutputPin, { /// diff --git a/mipidsi/src/graphics.rs b/mipidsi/src/graphics.rs index 0292d58..8a2d54e 100644 --- a/mipidsi/src/graphics.rs +++ b/mipidsi/src/graphics.rs @@ -10,13 +10,13 @@ use embedded_hal::digital::OutputPin; use crate::dcs::InterfaceExt; use crate::{dcs::BitsPerPixel, interface::Interface}; use crate::{dcs::WriteMemoryStart, models::Model}; -use crate::{interface::PixelFormat, Display}; +use crate::{interface::InterfacePixelFormat, Display}; impl DrawTarget for Display where DI: Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: InterfacePixelFormat, RST: OutputPin, { type Error = DI::Error; @@ -121,7 +121,7 @@ impl OriginDimensions for Display where DI: Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: InterfacePixelFormat, RST: OutputPin, { fn size(&self) -> Size { diff --git a/mipidsi/src/interface/mod.rs b/mipidsi/src/interface/mod.rs index 4e6a8bc..78e7f0b 100644 --- a/mipidsi/src/interface/mod.rs +++ b/mipidsi/src/interface/mod.rs @@ -77,7 +77,7 @@ fn rgb666_to_bytes(pixel: Rgb666) -> [u8; 3] { } /// This is an implementation detail, it should not be implemented or used outside this crate -pub trait PixelFormat { +pub trait InterfacePixelFormat { // this should just be // const N: usize; // fn convert(self) -> [Word; Self::N]; @@ -97,7 +97,7 @@ pub trait PixelFormat { ) -> Result<(), DI::Error>; } -impl PixelFormat for Rgb565 { +impl InterfacePixelFormat for Rgb565 { fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, @@ -114,7 +114,7 @@ impl PixelFormat for Rgb565 { } } -impl PixelFormat for Rgb666 { +impl InterfacePixelFormat for Rgb666 { fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, @@ -131,7 +131,7 @@ impl PixelFormat for Rgb666 { } } -impl PixelFormat for Rgb565 { +impl InterfacePixelFormat for Rgb565 { fn send_pixels>( di: &mut DI, pixels: impl IntoIterator, diff --git a/mipidsi/src/lib.rs b/mipidsi/src/lib.rs index 32efa6b..82ef368 100644 --- a/mipidsi/src/lib.rs +++ b/mipidsi/src/lib.rs @@ -108,7 +108,7 @@ use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; pub mod options; -use interface::PixelFormat; +use interface::InterfacePixelFormat; use options::MemoryMapping; mod builder; @@ -134,7 +134,7 @@ pub struct Display where DI: interface::Interface, MODEL: Model, - MODEL::ColorFormat: PixelFormat, + MODEL::ColorFormat: InterfacePixelFormat, RST: OutputPin, { // DCS provider @@ -155,7 +155,7 @@ impl Display where DI: interface::Interface, M: Model, - M::ColorFormat: PixelFormat, + M::ColorFormat: InterfacePixelFormat, RST: OutputPin, { ///