diff --git a/Cargo.toml b/Cargo.toml index d87511fb..9db68a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ branch = "master" repository = "jamwaffles/ssd1306" [dependencies] -embedded-hal = "0.2.2" +embedded-hal = "0.2.3" [dev-dependencies.stm32f1xx-hal] version = "0.2.0" diff --git a/examples/graphics.rs b/examples/graphics.rs index f37bd5a8..da65f916 100644 --- a/examples/graphics.rs +++ b/examples/graphics.rs @@ -76,7 +76,7 @@ fn main() -> ! { let mut disp: GraphicsMode<_> = Builder::new().connect_spi(spi, dc).into(); - disp.reset(&mut rst, &mut delay); + disp.reset(&mut rst, &mut delay).unwrap(); disp.init().unwrap(); disp.flush().unwrap(); diff --git a/examples/pixelsquare.rs b/examples/pixelsquare.rs index 24aa5d20..f8e0e402 100644 --- a/examples/pixelsquare.rs +++ b/examples/pixelsquare.rs @@ -75,7 +75,7 @@ fn main() -> ! { let mut disp: GraphicsMode<_> = Builder::new().connect_spi(spi, dc).into(); - disp.reset(&mut rst, &mut delay); + disp.reset(&mut rst, &mut delay).unwrap(); disp.init().unwrap(); disp.flush().unwrap(); diff --git a/src/builder.rs b/src/builder.rs index 94fd0a71..7814bc11 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; @@ -96,9 +96,9 @@ impl Builder { } /// 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), @@ -109,14 +109,15 @@ impl Builder { } /// 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, - DC: OutputPin, + SPI: hal::blocking::spi::Transfer + + hal::blocking::spi::Write, + DC: OutputPin, { let properties = DisplayProperties::new(SpiInterface::new(spi, dc), self.display_size, self.rotation); diff --git a/src/command.rs b/src/command.rs index 9d9f5110..dba63120 100644 --- a/src/command.rs +++ b/src/command.rs @@ -84,7 +84,7 @@ pub enum Command { impl Command { /// Send command to SSD1306 - pub fn send(self, iface: &mut DI) -> Result<(), ()> + pub fn send(self, iface: &mut DI) -> Result<(), DI::Error> where DI: DisplayInterface, { @@ -156,9 +156,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 3561fa80..8c068e09 100644 --- a/src/interface/i2c.rs +++ b/src/interface/i2c.rs @@ -3,6 +3,7 @@ use hal; use super::DisplayInterface; +use crate::Error; // TODO: Add to prelude /// SSD1306 I2C communication interface @@ -11,9 +12,9 @@ pub struct I2cInterface { addr: u8, } -impl I2cInterface +impl I2cInterface where - I2C: hal::blocking::i2c::Write, + I2C: hal::blocking::i2c::Write, { /// Create new SSD1306 I2C interface pub fn new(i2c: I2C, addr: u8) -> Self { @@ -21,23 +22,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[0..cmds.len()]); 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> { // Noop if the data buffer is empty if buf.is_empty() { return Ok(()); @@ -57,7 +58,7 @@ where self.i2c .write(self.addr, &writebuf[..=chunklen]) - .map_err(|_| ())?; + .map_err(Error::Comm)?; } Ok(()) diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 703842a7..4328491a 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -56,10 +56,12 @@ pub mod spi; /// A method of communicating with SSD1306 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 c36331b5..c0311d30 100644 --- a/src/interface/spi.rs +++ b/src/interface/spi.rs @@ -1,9 +1,10 @@ //! SSD1306 SPI interface use hal; -use hal::digital::OutputPin; +use hal::digital::v2::OutputPin; use super::DisplayInterface; +use crate::Error; // TODO: Add to prelude /// SPI display interface. @@ -14,10 +15,10 @@ pub struct SpiInterface { dc: DC, } -impl SpiInterface +impl SpiInterface where - SPI: hal::blocking::spi::Write, - DC: OutputPin, + SPI: hal::blocking::spi::Write, + DC: OutputPin, { /// Create new SPI interface for communciation with SSD1306 pub fn new(spi: SPI, dc: DC) -> Self { @@ -25,27 +26,25 @@ where } } -impl DisplayInterface for SpiInterface +impl DisplayInterface for SpiInterface where - SPI: hal::blocking::spi::Write, - DC: OutputPin, + SPI: hal::blocking::spi::Write, + DC: OutputPin, { - fn send_commands(&mut self, cmds: &[u8]) -> Result<(), ()> { - self.dc.set_low(); + type Error = Error; - self.spi.write(&cmds).map_err(|_| ())?; + fn send_commands(&mut self, cmds: &[u8]) -> Result<(), Self::Error> { + self.dc.set_low().map_err(Error::Pin)?; - self.dc.set_high(); + self.spi.write(&cmds).map_err(Error::Comm)?; - Ok(()) + self.dc.set_high().map_err(Error::Pin) } - fn send_data(&mut self, buf: &[u8]) -> Result<(), ()> { + fn send_data(&mut self, buf: &[u8]) -> Result<(), Self::Error> { // 1 = data, 0 = command - self.dc.set_high(); + self.dc.set_high().map_err(Error::Pin)?; - self.spi.write(&buf).map_err(|_| ())?; - - Ok(()) + self.spi.write(&buf).map_err(Error::Comm) } } diff --git a/src/lib.rs b/src/lib.rs index 7e776633..8379c31e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,6 +166,15 @@ #![deny(unused_import_braces)] #![deny(unused_qualifications)] +/// Errors in this crate +#[derive(Debug)] +pub enum Error { + /// Communication error + Comm(CommE), + /// Pin setting error + Pin(PinE), +} + extern crate embedded_hal as hal; pub mod builder; diff --git a/src/mode/graphics.rs b/src/mode/graphics.rs index c374bdea..55e958ca 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; // TODO: Add to prelude /// Graphics mode handler @@ -63,20 +64,24 @@ 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 - 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 @@ -144,9 +149,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 @@ -155,7 +159,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/mode/terminal.rs b/src/mode/terminal.rs index 95a9447a..d902197b 100644 --- a/src/mode/terminal.rs +++ b/src/mode/terminal.rs @@ -23,9 +23,10 @@ use crate::displaysize::DisplaySize; use crate::interface::DisplayInterface; use crate::mode::displaymode::DisplayModeTrait; use crate::properties::DisplayProperties; +use crate::Error; use core::fmt; use hal::blocking::delay::DelayMs; -use hal::digital::OutputPin; +use hal::digital::v2::OutputPin; /// A trait to convert from a character to 8x8 bitmap pub trait CharacterBitmap { @@ -165,7 +166,7 @@ where DI: DisplayInterface, { /// Clear the display - pub fn clear(&mut self) -> Result<(), ()> { + pub fn clear(&mut self) -> Result<(), DI::Error> { let display_size = self.properties.get_size(); let numchars = match display_size { @@ -187,16 +188,20 @@ where } /// Reset display - 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. This is a noop in terminal mode. @@ -205,24 +210,22 @@ where } /// Print a character to the display - pub fn print_char(&mut self, c: T) -> Result<(), ()> + pub fn print_char(&mut self, c: T) -> Result<(), DI::Error> where TerminalMode: CharacterBitmap, { // Send the pixel data to the display - self.properties.draw(&Self::to_bitmap(c))?; - Ok(()) + self.properties.draw(&Self::to_bitmap(c)) } /// 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(&mut self) -> Result<(), ()> { - self.properties.init_column_mode()?; - Ok(()) + pub fn init(&mut self) -> Result<(), DI::Error> { + self.properties.init_column_mode() } /// 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 1639a70a..3f1c80ce 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -31,7 +31,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(); @@ -60,26 +60,22 @@ where Command::AllOn(false).send(&mut self.iface)?; Command::Invert(false).send(&mut self.iface)?; Command::EnableScroll(false).send(&mut self.iface)?; - Command::DisplayOn(true).send(&mut self.iface)?; - - Ok(()) + Command::DisplayOn(true).send(&mut self.iface) } /// 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> { Command::ColumnAddress(start.0, end.0 - 1).send(&mut self.iface)?; - Command::PageAddress(start.1.into(), (end.1 - 1).into()).send(&mut self.iface)?; - Ok(()) + Command::PageAddress(start.1.into(), (end.1 - 1).into()).send(&mut self.iface) } /// 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, buffer: &[u8]) -> Result<(), ()> { - self.iface.send_data(&buffer)?; - Ok(()) + pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DI::Error> { + self.iface.send_data(&buffer) } /// Get the configured display size @@ -131,28 +127,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(()) + } } }