diff --git a/i2c/src/lib.rs b/i2c/src/lib.rs index a6a562c..9a42df2 100644 --- a/i2c/src/lib.rs +++ b/i2c/src/lib.rs @@ -3,7 +3,7 @@ //! Generic I2C interface for display drivers use embedded_hal as hal; -use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; +use display_interface::{DisplayError, WriteOnlyDataCommand}; /// I2C communication interface pub struct I2CInterface { @@ -30,81 +30,60 @@ where pub fn release(self) -> I2C { self.i2c } -} - -impl WriteOnlyDataCommand for I2CInterface -where - I2C: hal::blocking::i2c::Write, -{ - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - // Copy over given commands to new aray to prefix with command identifier - match cmds { - DataFormat::U8(slice) => { - let mut writebuf: [u8; 8] = [0; 8]; - writebuf[1..=slice.len()].copy_from_slice(&slice[0..slice.len()]); - - self.i2c - .write(self.addr, &writebuf[..=slice.len()]) - .map_err(|_| DisplayError::BusWriteError) - } - _ => Err(DisplayError::DataFormatNotImplemented), - } - } - - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - match buf { - DataFormat::U8(slice) => { - // No-op if the data buffer is empty - if slice.is_empty() { - return Ok(()); - } - - let mut writebuf = [0; 17]; - // Data mode - writebuf[0] = self.data_byte; + fn send_iter( + &mut self, + first_byte: u8, + data: impl Iterator, + ) -> Result<(), DisplayError> { + let mut writebuf = [0; 17]; + let mut i = 1; + let len = writebuf.len(); - slice - .chunks(16) - .try_for_each(|c| { - let chunk_len = c.len(); + // Data/command mode + writebuf[0] = first_byte; - // Copy over all data from buffer, leaving the data command byte intact - writebuf[1..=chunk_len].copy_from_slice(c); + for byte in data { + writebuf[i] = byte; + i += 1; - self.i2c.write(self.addr, &writebuf[0..=chunk_len]) - }) - .map_err(|_| DisplayError::BusWriteError) + if i == len { + self.i2c + .write(self.addr, &writebuf[0..=len]) + .map_err(|_| DisplayError::BusWriteError)?; + i = 1; } - DataFormat::U8Iter(iter) => { - let mut writebuf = [0; 17]; - let mut i = 1; - let len = writebuf.len(); - - // Data mode - writebuf[0] = self.data_byte; + } - for byte in iter.into_iter() { - writebuf[i] = byte; - i += 1; + if i > 1 { + self.i2c + .write(self.addr, &writebuf[0..=i]) + .map_err(|_| DisplayError::BusWriteError)?; + } - if i == len { - self.i2c - .write(self.addr, &writebuf[0..=len]) - .map_err(|_| DisplayError::BusWriteError)?; - i = 1; - } - } + Ok(()) + } +} - if i > 1 { - self.i2c - .write(self.addr, &writebuf[0..=i]) - .map_err(|_| DisplayError::BusWriteError)?; - } +impl WriteOnlyDataCommand for I2CInterface +where + I2C: hal::blocking::i2c::Write, +{ + type Word = u8; + + #[inline] + fn send_command_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + self.send_iter(0, iter) + } - Ok(()) - } - _ => Err(DisplayError::DataFormatNotImplemented), - } + #[inline] + fn send_data_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + self.send_iter(self.data_byte, iter) } } diff --git a/parallel-gpio/Cargo.toml b/parallel-gpio/Cargo.toml index feeb092..07a0132 100644 --- a/parallel-gpio/Cargo.toml +++ b/parallel-gpio/Cargo.toml @@ -20,5 +20,4 @@ all-features = true [dependencies] embedded-hal = "0.2.3" display-interface = "0.4" -byte-slice-cast = { version = "0.3.5", default-features = false } diff --git a/parallel-gpio/src/lib.rs b/parallel-gpio/src/lib.rs index cda64ea..f652ace 100644 --- a/parallel-gpio/src/lib.rs +++ b/parallel-gpio/src/lib.rs @@ -4,7 +4,7 @@ use embedded_hal::digital::v2::OutputPin; -pub use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; +pub use display_interface::{DisplayError, WriteOnlyDataCommand}; /// This trait represents the data pins of a parallel bus. /// @@ -108,24 +108,48 @@ generic_bus! { } } -/// Parallel 8 Bit communication interface +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 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 an "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, + BUS: OutputBus, DC: OutputPin, WR: OutputPin, { @@ -140,140 +164,45 @@ where (self.bus, self.dc, self.wr) } - fn set_value(self: &mut Self, value: u8) -> Result<(), DisplayError> { + fn set_value(self: &mut Self, value: BUS::Word) -> Result<(), DisplayError> { self.bus.set_value(value) } + + fn send_iter>( + &mut self, + mut iter: I, + ) -> Result<(), DisplayError> { + iter.try_for_each(|d| { + self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; + self.set_value(d)?; + self.wr.set_high().map_err(|_| DisplayError::BusWriteError) + }) + } } -impl WriteOnlyDataCommand for PGPIO8BitInterface +impl WriteOnlyDataCommand for ParallelInterface where - BUS: OutputBus, + BUS: OutputBus, DC: OutputPin, WR: OutputPin, { - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - use byte_slice_cast::*; - self.dc.set_low().map_err(|_| DisplayError::DCError)?; - match cmds { - DataFormat::U8(slice) => slice.iter().try_for_each(|cmd| { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr.set_high().map_err(|_| DisplayError::BusWriteError) - }), - DataFormat::U8Iter(iter) => { - for c in iter.into_iter() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(c)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } + type Word = BUS::Word; - Ok(()) - } - DataFormat::U16(slice) => slice.as_byte_slice().iter().try_for_each(|cmd| { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr.set_high().map_err(|_| DisplayError::BusWriteError) - }), - DataFormat::U16LE(slice) => slice.iter().try_for_each(|cmd| { - for cmd in &cmd.to_le_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) - }), - DataFormat::U16BE(slice) => slice.iter().try_for_each(|cmd| { - for cmd in &cmd.to_be_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } + fn send_command_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + self.dc.set_low().map_err(|_| DisplayError::DCError)?; - Ok(()) - }), - _ => Err(DisplayError::DataFormatNotImplemented), - } + self.send_iter(iter) } - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - use byte_slice_cast::*; + fn send_data_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { self.dc.set_high().map_err(|_| DisplayError::DCError)?; - match buf { - DataFormat::U8(slice) => slice.iter().try_for_each(|d| { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*d)?; - self.wr.set_high().map_err(|_| DisplayError::BusWriteError) - }), - DataFormat::U16(slice) => slice.as_byte_slice().iter().try_for_each(|d| { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*d)?; - self.wr.set_high().map_err(|_| DisplayError::BusWriteError) - }), - DataFormat::U16LE(slice) => slice.iter().try_for_each(|cmd| { - for cmd in &cmd.to_le_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) - }), - DataFormat::U16BE(slice) => slice.iter().try_for_each(|cmd| { - for cmd in &cmd.to_be_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) - }), - DataFormat::U8Iter(iter) => { - for d in iter.into_iter() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(d)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - Ok(()) - } - DataFormat::U16LEIter(iter) => { - for cmd in iter.into_iter() { - for cmd in &cmd.to_le_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - } - Ok(()) - } - DataFormat::U16BEIter(iter) => { - for cmd in iter.into_iter() { - for cmd in &cmd.to_be_bytes() { - self.wr.set_low().map_err(|_| DisplayError::BusWriteError)?; - self.set_value(*cmd)?; - self.wr - .set_high() - .map_err(|_| DisplayError::BusWriteError)?; - } - } - Ok(()) - } - _ => Err(DisplayError::DataFormatNotImplemented), - } + self.send_iter(iter) } } diff --git a/spi/Cargo.toml b/spi/Cargo.toml index b6013af..d10e2aa 100644 --- a/spi/Cargo.toml +++ b/spi/Cargo.toml @@ -20,4 +20,3 @@ all-features = true [dependencies] embedded-hal = "0.2.3" display-interface = "0.4" -byte-slice-cast = { version = "0.3.5", default-features = false } diff --git a/spi/src/lib.rs b/spi/src/lib.rs index 632ad25..cbe028e 100644 --- a/spi/src/lib.rs +++ b/spi/src/lib.rs @@ -5,105 +5,31 @@ use embedded_hal as hal; use hal::digital::v2::OutputPin; -use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; +use display_interface::{DisplayError, WriteOnlyDataCommand}; -fn send_u8>( - spi: &mut SPI, - words: DataFormat<'_>, +fn send_iter( + spi: &mut impl hal::blocking::spi::Write, + iter: impl Iterator, ) -> Result<(), DisplayError> { - match words { - DataFormat::U8(slice) => spi.write(slice).map_err(|_| DisplayError::BusWriteError), - DataFormat::U16(slice) => { - use byte_slice_cast::*; - spi.write(slice.as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError) - } - DataFormat::U16LE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_le(); - } - spi.write(slice.as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError) - } - DataFormat::U16BE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_be(); - } - spi.write(slice.as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError) - } - DataFormat::U8Iter(iter) => { - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.into_iter() { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf).map_err(|_| DisplayError::BusWriteError)?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i]) - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) - } - DataFormat::U16LEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.map(u16::to_le) { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf.as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError)?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) - } - DataFormat::U16BEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 64]; - let mut i = 0; - let len = buf.len(); - - for v in iter.map(u16::to_be) { - buf[i] = v; - i += 1; - - if i == len { - spi.write(&buf.as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError)?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice()) - .map_err(|_| DisplayError::BusWriteError)?; - } - - Ok(()) + let mut buf = [0; 32]; + let mut i = 0; + + for v in iter { + buf[i] = v; + i += 1; + + if i == buf.len() { + spi.write(&buf).map_err(|_| DisplayError::BusWriteError)?; + i = 0; } - _ => Err(DisplayError::DataFormatNotImplemented), } + + if i > 0 { + spi.write(&buf[..i]) + .map_err(|_| DisplayError::BusWriteError)?; + } + + Ok(()) } /// SPI display interface. @@ -139,7 +65,50 @@ where DC: OutputPin, CS: OutputPin, { - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { + type Word = u8; + + #[inline] + fn send_command_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + // Assert chip select pin + self.cs.set_low().map_err(|_| DisplayError::CSError)?; + + // 1 = data, 0 = command + self.dc.set_low().map_err(|_| DisplayError::DCError)?; + + // Send words over SPI + let err = send_iter(&mut self.spi, iter); + + // Deassert chip select pin + self.cs.set_high().ok(); + + err + } + + #[inline] + fn send_data_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + // Assert chip select pin + self.cs.set_low().map_err(|_| DisplayError::CSError)?; + + // 1 = data, 0 = command + self.dc.set_high().map_err(|_| DisplayError::DCError)?; + + // Send words over SPI + let err = send_iter(&mut self.spi, iter); + + // Deassert chip select pin + self.cs.set_high().ok(); + + err + } + + #[inline] + fn send_command_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { // Assert chip select pin self.cs.set_low().map_err(|_| DisplayError::CSError)?; @@ -147,7 +116,10 @@ where self.dc.set_low().map_err(|_| DisplayError::DCError)?; // Send words over SPI - let err = send_u8(&mut self.spi, cmds); + let err = self + .spi + .write(slice) + .map_err(|_| DisplayError::BusWriteError); // Deassert chip select pin self.cs.set_high().ok(); @@ -155,7 +127,8 @@ where err } - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { + #[inline] + fn send_data_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { // Assert chip select pin self.cs.set_low().map_err(|_| DisplayError::CSError)?; @@ -163,7 +136,10 @@ where self.dc.set_high().map_err(|_| DisplayError::DCError)?; // Send words over SPI - let err = send_u8(&mut self.spi, buf); + let err = self + .spi + .write(slice) + .map_err(|_| DisplayError::BusWriteError); // Deassert chip select pin self.cs.set_high().ok(); @@ -202,19 +178,51 @@ where SPI: hal::blocking::spi::Write, DC: OutputPin, { - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { + type Word = u8; + + #[inline] + fn send_command_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + // 1 = data, 0 = command + self.dc.set_low().map_err(|_| DisplayError::DCError)?; + + // Send words over SPI + send_iter(&mut self.spi, iter) + } + + #[inline] + fn send_data_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError> { + // 1 = data, 0 = command + self.dc.set_high().map_err(|_| DisplayError::DCError)?; + + // Send words over SPI + send_iter(&mut self.spi, iter) + } + + #[inline] + fn send_command_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { // 1 = data, 0 = command self.dc.set_low().map_err(|_| DisplayError::DCError)?; // Send words over SPI - send_u8(&mut self.spi, cmds) + self.spi + .write(slice) + .map_err(|_| DisplayError::BusWriteError) } - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { + #[inline] + fn send_data_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { // 1 = data, 0 = command self.dc.set_high().map_err(|_| DisplayError::DCError)?; // Send words over SPI - send_u8(&mut self.spi, buf) + self.spi + .write(slice) + .map_err(|_| DisplayError::BusWriteError) } } diff --git a/src/lib.rs b/src/lib.rs index 496f55e..0f6cf3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,50 +14,41 @@ pub mod prelude; #[derive(Clone, Debug)] #[non_exhaustive] pub enum DisplayError { - /// Invalid data format selected for interface selected - InvalidFormatError, /// Unable to write to bus BusWriteError, /// Unable to assert or de-assert data/command switching signal DCError, /// Unable to assert chip select signal CSError, - /// The requested DataFormat is not implemented by this display interface implementation - DataFormatNotImplemented, /// Unable to assert or de-assert reset signal RSError, /// Attempted to write to a non-existing pixel outside the display's bounds OutOfBoundsError, } -/// DI specific data format wrapper around slices of various widths -/// Display drivers need to implement non-trivial conversions (e.g. with padding) -/// as the hardware requires. -#[non_exhaustive] -pub enum DataFormat<'a> { - /// Slice of unsigned bytes - U8(&'a [u8]), - /// Slice of unsigned 16bit values with the same endianess as the system, not recommended - U16(&'a [u16]), - /// Slice of unsigned 16bit values to be sent in big endian byte order - U16BE(&'a mut [u16]), - /// Slice of unsigned 16bit values to be sent in little endian byte order - U16LE(&'a mut [u16]), - /// Iterator over unsigned bytes - U8Iter(&'a mut dyn Iterator), - /// Iterator over unsigned 16bit values to be sent in big endian byte order - U16BEIter(&'a mut dyn Iterator), - /// Iterator over unsigned 16bit values to be sent in little endian byte order - U16LEIter(&'a mut dyn Iterator), -} - /// This trait implements a write-only interface for a display which has separate data and command /// modes. It is the responsibility of implementations to activate the correct mode in their /// implementation when corresponding method is called. pub trait WriteOnlyDataCommand { - /// Send a batch of commands to display - fn send_commands(&mut self, cmd: DataFormat<'_>) -> Result<(), DisplayError>; + type Word: Copy; + + fn send_command_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError>; + + fn send_data_iter( + &mut self, + iter: impl Iterator, + ) -> Result<(), DisplayError>; + + #[inline] + fn send_command_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { + self.send_command_iter(slice.iter().copied()) + } - /// Send pixel data to display - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError>; + #[inline] + fn send_data_slice(&mut self, slice: &[Self::Word]) -> Result<(), DisplayError> { + self.send_data_iter(slice.iter().copied()) + } }