From ddd17ea6bb3e25a0c299896c4598cbf8d5f6e1c4 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Mon, 10 Jun 2019 12:38:47 +0200 Subject: [PATCH 1/2] Forward communication errors --- src/builder.rs | 9 +++++---- src/command.rs | 6 ++---- src/interface/i2c.rs | 19 ++++++++++--------- src/interface/mod.rs | 6 ++++-- src/interface/spi.rs | 19 +++++++++++-------- src/lib.rs | 7 +++++++ src/mode/graphics.rs | 9 ++++----- src/properties.rs | 30 ++++++++++++------------------ 8 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 7d642e6..bb99220 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -116,9 +116,9 @@ where } /// Finish the builder and use I2C to communicate with the display - pub fn connect_i2c(self, i2c: I2C) -> DisplayMode>> + pub fn connect_i2c(self, i2c: I2C) -> DisplayMode>> where - I2C: hal::blocking::i2c::Write, + I2C: hal::blocking::i2c::Write, { let properties = DisplayProperties::new( I2cInterface::new(i2c, self.i2c_addr), @@ -129,13 +129,14 @@ where } /// Finish the builder and use SPI to communicate with the display - pub fn connect_spi( + pub fn connect_spi( self, spi: SPI, dc: DC, ) -> DisplayMode>> where - SPI: hal::blocking::spi::Transfer + hal::blocking::spi::Write, + SPI: hal::blocking::spi::Transfer + + hal::blocking::spi::Write, DC: OutputPin, { let properties = DisplayProperties::new( diff --git a/src/command.rs b/src/command.rs index 99f225a..9a96f9e 100644 --- a/src/command.rs +++ b/src/command.rs @@ -51,7 +51,7 @@ pub enum Command { impl Command { /// Send command to sh1106 - pub fn send(self, iface: &mut DI) -> Result<(), ()> + pub fn send(self, iface: &mut DI) -> Result<(), DI::Error> where DI: DisplayInterface, { @@ -83,9 +83,7 @@ impl Command { }; // Send command over the interface - iface.send_commands(&data[0..len])?; - - Ok(()) + iface.send_commands(&data[0..len]) } } diff --git a/src/interface/i2c.rs b/src/interface/i2c.rs index 74c9ef1..5f7c449 100644 --- a/src/interface/i2c.rs +++ b/src/interface/i2c.rs @@ -4,6 +4,7 @@ use hal; use super::DisplayInterface; use crate::command::Page; +use crate::Error; // TODO: Add to prelude /// sh1106 I2C communication interface @@ -22,23 +23,23 @@ where } } -impl DisplayInterface for I2cInterface +impl DisplayInterface for I2cInterface where - I2C: hal::blocking::i2c::Write, + I2C: hal::blocking::i2c::Write, { - fn send_commands(&mut self, cmds: &[u8]) -> Result<(), ()> { + type Error = Error; + + fn send_commands(&mut self, cmds: &[u8]) -> Result<(), Self::Error> { // Copy over given commands to new aray to prefix with command identifier let mut writebuf: [u8; 8] = [0; 8]; writebuf[1..=cmds.len()].copy_from_slice(&cmds); self.i2c .write(self.addr, &writebuf[..=cmds.len()]) - .map_err(|_| ())?; - - Ok(()) + .map_err(Error::Comm) } - fn send_data(&mut self, buf: &[u8]) -> Result<(), ()> { + fn send_data(&mut self, buf: &[u8]) -> Result<(), Self::Error> { // Display is always 128px wide const CHUNKLEN: usize = 128; @@ -70,9 +71,9 @@ where 0x10, // Upper column address (always zero, base is 10h) ], ) - .map_err(|_| ())?; + .map_err(Error::Comm)?; - self.i2c.write(self.addr, &writebuf).map_err(|_| ())?; + self.i2c.write(self.addr, &writebuf).map_err(Error::Comm)?; page += 1; } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 7c58a44..0bde8e1 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -52,10 +52,12 @@ pub mod spi; /// A method of communicating with sh1106 pub trait DisplayInterface { + /// Interface error type + type Error; /// Send a batch of up to 8 commands to display. - fn send_commands(&mut self, cmd: &[u8]) -> Result<(), ()>; + fn send_commands(&mut self, cmd: &[u8]) -> Result<(), Self::Error>; /// Send data to display. - fn send_data(&mut self, buf: &[u8]) -> Result<(), ()>; + fn send_data(&mut self, buf: &[u8]) -> Result<(), Self::Error>; } pub use self::i2c::I2cInterface; diff --git a/src/interface/spi.rs b/src/interface/spi.rs index c7b515e..c174911 100644 --- a/src/interface/spi.rs +++ b/src/interface/spi.rs @@ -4,6 +4,7 @@ use hal; use hal::digital::OutputPin; use super::DisplayInterface; +use crate::Error; /// SPI display interface. /// @@ -14,9 +15,9 @@ pub struct SpiInterface { cs: CS, } -impl SpiInterface +impl SpiInterface where - SPI: hal::blocking::spi::Write, + SPI: hal::blocking::spi::Write, DC: OutputPin, CS: OutputPin, { @@ -28,17 +29,19 @@ where } } -impl DisplayInterface for SpiInterface +impl DisplayInterface for SpiInterface where - SPI: hal::blocking::spi::Write, + SPI: hal::blocking::spi::Write, DC: OutputPin, CS: OutputPin, { - fn send_commands(&mut self, cmds: &[u8]) -> Result<(), ()> { + type Error = Error; + + fn send_commands(&mut self, cmds: &[u8]) -> Result<(), Self::Error> { self.cs.set_low(); self.dc.set_low(); - self.spi.write(&cmds).map_err(|_| ())?; + self.spi.write(&cmds).map_err(Error::Comm)?; self.dc.set_high(); self.cs.set_high(); @@ -46,13 +49,13 @@ where Ok(()) } - fn send_data(&mut self, buf: &[u8]) -> Result<(), ()> { + fn send_data(&mut self, buf: &[u8]) -> Result<(), Self::Error> { self.cs.set_low(); // 1 = data, 0 = command self.dc.set_high(); - self.spi.write(&buf).map_err(|_| ())?; + self.spi.write(&buf).map_err(Error::Comm)?; self.cs.set_high(); diff --git a/src/lib.rs b/src/lib.rs index 6e3a8d1..98b3cd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,13 @@ #![deny(unused_import_braces)] #![deny(unused_qualifications)] +/// Errors in this crate +#[derive(Debug)] +pub enum Error { + /// Communication error + Comm(CommE), +} + extern crate embedded_hal as hal; pub mod builder; diff --git a/src/mode/graphics.rs b/src/mode/graphics.rs index 6366f5b..b9663f1 100644 --- a/src/mode/graphics.rs +++ b/src/mode/graphics.rs @@ -78,7 +78,7 @@ where } /// Write out data to display - pub fn flush(&mut self) -> Result<(), ()> { + pub fn flush(&mut self) -> Result<(), DI::Error> { let display_size = self.properties.get_size(); // Ensure the display buffer is at the origin of the display before we send the full frame @@ -147,9 +147,8 @@ where /// Display is set up in column mode, i.e. a byte walks down a column of 8 pixels from /// column 0 on the left, to column _n_ on the right - pub fn init(&mut self) -> Result<(), ()> { - self.properties.init_column_mode()?; - Ok(()) + pub fn init(&mut self) -> Result<(), DI::Error> { + self.properties.init_column_mode() } /// Get display dimensions, taking into account the current rotation of the display @@ -158,7 +157,7 @@ where } /// Set the display rotation - pub fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), ()> { + pub fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), DI::Error> { self.properties.set_rotation(rot) } } diff --git a/src/properties.rs b/src/properties.rs index c8a2313..e1d2280 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -39,7 +39,7 @@ where /// Initialise the display in column mode (i.e. a byte walks down a column of 8 pixels) with /// column 0 on the left and column _(display_width - 1)_ on the right. - pub fn init_column_mode(&mut self) -> Result<(), ()> { + pub fn init_column_mode(&mut self) -> Result<(), DI::Error> { // TODO: Break up into nice bits so display modes can pick whathever they need let (_, display_height) = self.display_size.dimensions(); let display_rotation = self.display_rotation; @@ -74,21 +74,19 @@ where /// Set the position in the framebuffer of the display where any sent data should be /// drawn. This method can be used for changing the affected area on the screen as well /// as (re-)setting the start point of the next `draw` call. - pub fn set_draw_area(&mut self, start: (u8, u8), end: (u8, u8)) -> Result<(), ()> { + pub fn set_draw_area(&mut self, start: (u8, u8), end: (u8, u8)) -> Result<(), DI::Error> { self.draw_area_start = start; self.draw_area_end = end; self.draw_column = start.0; self.draw_row = start.1; - self.send_draw_address()?; - - Ok(()) + self.send_draw_address() } /// Send the data to the display for drawing at the current position in the framebuffer /// and advance the position accordingly. Cf. `set_draw_area` to modify the affected area by /// this method. - pub fn draw(&mut self, mut buffer: &[u8]) -> Result<(), ()> { + pub fn draw(&mut self, mut buffer: &[u8]) -> Result<(), DI::Error> { while buffer.len() > 0 { let count = self.draw_area_end.0 - self.draw_column; self.iface.send_data(&buffer[..count as usize])?; @@ -111,12 +109,10 @@ where Ok(()) } - fn send_draw_address(&mut self) -> Result<(), ()> { + fn send_draw_address(&mut self) -> Result<(), DI::Error> { Command::PageAddress(self.draw_row.into()).send(&mut self.iface)?; Command::ColumnAddressLow(0xF & self.draw_column).send(&mut self.iface)?; - Command::ColumnAddressHigh(0xF & (self.draw_column >> 4)).send(&mut self.iface)?; - - Ok(()) + Command::ColumnAddressHigh(0xF & (self.draw_column >> 4)).send(&mut self.iface) } /// Get the configured display size @@ -168,28 +164,26 @@ where } /// Set the display rotation - pub fn set_rotation(&mut self, display_rotation: DisplayRotation) -> Result<(), ()> { + pub fn set_rotation(&mut self, display_rotation: DisplayRotation) -> Result<(), DI::Error> { self.display_rotation = display_rotation; match display_rotation { DisplayRotation::Rotate0 => { Command::SegmentRemap(true).send(&mut self.iface)?; - Command::ReverseComDir(true).send(&mut self.iface)?; + Command::ReverseComDir(true).send(&mut self.iface) } DisplayRotation::Rotate90 => { Command::SegmentRemap(false).send(&mut self.iface)?; - Command::ReverseComDir(true).send(&mut self.iface)?; + Command::ReverseComDir(true).send(&mut self.iface) } DisplayRotation::Rotate180 => { Command::SegmentRemap(false).send(&mut self.iface)?; - Command::ReverseComDir(false).send(&mut self.iface)?; + Command::ReverseComDir(false).send(&mut self.iface) } DisplayRotation::Rotate270 => { Command::SegmentRemap(true).send(&mut self.iface)?; - Command::ReverseComDir(false).send(&mut self.iface)?; + Command::ReverseComDir(false).send(&mut self.iface) } - }; - - Ok(()) + } } } From e910a46383eda0a36efccb43c7d65a002f336eb4 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Mon, 10 Jun 2019 12:39:57 +0200 Subject: [PATCH 2/2] Use fallible OutputPin traits --- Cargo.toml | 2 +- src/builder.rs | 18 ++++++++++++------ src/interface/i2c.rs | 6 +++++- src/interface/mod.rs | 3 +++ src/interface/spi.rs | 42 ++++++++++++++++++++---------------------- src/lib.rs | 4 +++- src/mode/graphics.rs | 17 +++++++++++------ src/properties.rs | 1 + 8 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index df950ee..e8b275d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ branch = "master" repository = "jamwaffles/sh1106" [dependencies] -embedded-hal = "0.2.2" +embedded-hal = "0.2.3" [dependencies.embedded-graphics] optional = true diff --git a/src/builder.rs b/src/builder.rs index bb99220..0d82e6a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -41,7 +41,7 @@ //! ``` use hal; -use hal::digital::OutputPin; +use hal::digital::v2::OutputPin; use crate::displayrotation::DisplayRotation; use crate::displaysize::DisplaySize; @@ -77,9 +77,9 @@ impl Builder { } } -impl Builder +impl Builder where - CS: OutputPin, + CS: OutputPin, { /// Set the size of the display. Supported sizes are defined by [DisplaySize]. pub fn with_size(self, display_size: DisplaySize) -> Self { @@ -137,7 +137,8 @@ where where SPI: hal::blocking::spi::Transfer + hal::blocking::spi::Write, - DC: OutputPin, + DC: OutputPin, + CS: OutputPin, { let properties = DisplayProperties::new( SpiInterface::new(spi, dc, self.spi_cs), @@ -153,6 +154,11 @@ where pub struct NoOutputPin; impl OutputPin for NoOutputPin { - fn set_low(&mut self) {} - fn set_high(&mut self) {} + type Error = (); + fn set_low(&mut self) -> Result<(), ()> { + Ok(()) + } + fn set_high(&mut self) -> Result<(), ()> { + Ok(()) + } } diff --git a/src/interface/i2c.rs b/src/interface/i2c.rs index 5f7c449..0112b7a 100644 --- a/src/interface/i2c.rs +++ b/src/interface/i2c.rs @@ -27,7 +27,11 @@ impl DisplayInterface for I2cInterface where I2C: hal::blocking::i2c::Write, { - type Error = Error; + type Error = Error; + + fn init(&mut self) -> Result<(), Self::Error> { + Ok(()) + } fn send_commands(&mut self, cmds: &[u8]) -> Result<(), Self::Error> { // Copy over given commands to new aray to prefix with command identifier diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 0bde8e1..488804f 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -54,6 +54,9 @@ pub mod spi; pub trait DisplayInterface { /// Interface error type type Error; + + /// Initialize device. + fn init(&mut self) -> Result<(), Self::Error>; /// Send a batch of up to 8 commands to display. fn send_commands(&mut self, cmd: &[u8]) -> Result<(), Self::Error>; /// Send data to display. diff --git a/src/interface/spi.rs b/src/interface/spi.rs index c174911..5994df4 100644 --- a/src/interface/spi.rs +++ b/src/interface/spi.rs @@ -1,7 +1,7 @@ //! sh1106 SPI interface use hal; -use hal::digital::OutputPin; +use hal::digital::v2::OutputPin; use super::DisplayInterface; use crate::Error; @@ -15,50 +15,48 @@ pub struct SpiInterface { cs: CS, } -impl SpiInterface +impl SpiInterface where SPI: hal::blocking::spi::Write, - DC: OutputPin, - CS: OutputPin, + DC: OutputPin, + CS: OutputPin, { /// Create new SPI interface for communciation with sh1106 - pub fn new(spi: SPI, dc: DC, mut cs: CS) -> Self { - cs.set_high(); - + pub fn new(spi: SPI, dc: DC, cs: CS) -> Self { Self { spi, dc, cs } } } -impl DisplayInterface for SpiInterface +impl DisplayInterface for SpiInterface where SPI: hal::blocking::spi::Write, - DC: OutputPin, - CS: OutputPin, + DC: OutputPin, + CS: OutputPin, { - type Error = Error; + type Error = Error; + + fn init(&mut self) -> Result<(), Self::Error> { + self.cs.set_high().map_err(Error::Pin) + } fn send_commands(&mut self, cmds: &[u8]) -> Result<(), Self::Error> { - self.cs.set_low(); - self.dc.set_low(); + self.cs.set_low().map_err(Error::Pin)?; + self.dc.set_low().map_err(Error::Pin)?; self.spi.write(&cmds).map_err(Error::Comm)?; - self.dc.set_high(); - self.cs.set_high(); - - Ok(()) + self.dc.set_high().map_err(Error::Pin)?; + self.cs.set_high().map_err(Error::Pin) } fn send_data(&mut self, buf: &[u8]) -> Result<(), Self::Error> { - self.cs.set_low(); + self.cs.set_low().map_err(Error::Pin)?; // 1 = data, 0 = command - self.dc.set_high(); + self.dc.set_high().map_err(Error::Pin)?; self.spi.write(&buf).map_err(Error::Comm)?; - self.cs.set_high(); - - Ok(()) + self.cs.set_high().map_err(Error::Pin) } } diff --git a/src/lib.rs b/src/lib.rs index 98b3cd7..62d3b84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,9 +98,11 @@ /// Errors in this crate #[derive(Debug)] -pub enum Error { +pub enum Error { /// Communication error Comm(CommE), + /// Pin setting error + Pin(PinE), } extern crate embedded_hal as hal; diff --git a/src/mode/graphics.rs b/src/mode/graphics.rs index b9663f1..d45e5af 100644 --- a/src/mode/graphics.rs +++ b/src/mode/graphics.rs @@ -16,13 +16,14 @@ //! ``` use hal::blocking::delay::DelayMs; -use hal::digital::OutputPin; +use hal::digital::v2::OutputPin; use crate::displayrotation::DisplayRotation; use crate::displaysize::DisplaySize; use crate::interface::DisplayInterface; use crate::mode::displaymode::DisplayModeTrait; use crate::properties::DisplayProperties; +use crate::Error; const BUFFER_SIZE: usize = 132 * 64 / 8; @@ -65,16 +66,20 @@ where /// Reset display // TODO: Move to a more appropriate place - pub fn reset(&mut self, rst: &mut RST, delay: &mut DELAY) + pub fn reset( + &mut self, + rst: &mut RST, + delay: &mut DELAY, + ) -> Result<(), Error<(), PinE>> where - RST: OutputPin, + RST: OutputPin, DELAY: DelayMs, { - rst.set_high(); + rst.set_high().map_err(Error::Pin)?; delay.delay_ms(1); - rst.set_low(); + rst.set_low().map_err(Error::Pin)?; delay.delay_ms(10); - rst.set_high(); + rst.set_high().map_err(Error::Pin) } /// Write out data to display diff --git a/src/properties.rs b/src/properties.rs index e1d2280..fa3716e 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -40,6 +40,7 @@ where /// Initialise the display in column mode (i.e. a byte walks down a column of 8 pixels) with /// column 0 on the left and column _(display_width - 1)_ on the right. pub fn init_column_mode(&mut self) -> Result<(), DI::Error> { + self.iface.init()?; // TODO: Break up into nice bits so display modes can pick whathever they need let (_, display_height) = self.display_size.dimensions(); let display_rotation = self.display_rotation;