diff --git a/Cargo.toml b/Cargo.toml index 016db030..1b1256ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ embedded-hal = "0.2.5" display-interface = "0.4.1" display-interface-i2c = "0.4.0" display-interface-spi = "0.4.1" -embedded-graphics-core = { version = "0.3.2", optional = true } +embedded-graphics = "0.7.1" [dev-dependencies] cortex-m = "0.7.2" @@ -38,10 +38,6 @@ embedded-graphics = "0.7.1" rand = { version = "0.8.4", default-features = false, features = [ "small_rng" ] } stm32f1xx-hal = { version = "0.7.0", features = [ "rt", "stm32f103" ] } -[features] -default = ["graphics"] -graphics = ["embedded-graphics-core"] - [profile.dev] codegen-units = 1 incremental = false @@ -50,3 +46,6 @@ incremental = false codegen-units = 1 debug = true lto = true + +[patch.crates-io] +embedded-graphics = { path = "../embedded-graphics" } diff --git a/examples/rotation_i2c.rs b/examples/rotation_i2c.rs index e4a72465..4137a5d5 100644 --- a/examples/rotation_i2c.rs +++ b/examples/rotation_i2c.rs @@ -73,6 +73,7 @@ fn main() -> ! { let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate90) .into_buffered_graphics_mode(); display.init().unwrap(); + display.flush().unwrap(); // Contrived example to test builder and instance methods. Sets rotation to 270 degress // or 90 degress counterclockwise diff --git a/src/lib.rs b/src/lib.rs index 7b710ab9..b0483050 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,12 +125,20 @@ use brightness::Brightness; use command::{AddrMode, Command, VcomhLevel}; use display_interface::{DataFormat::U8, DisplayError, WriteOnlyDataCommand}; use display_interface_spi::{SPIInterface, SPIInterfaceNoCS}; +use embedded_graphics::{ + framebuffer::Framebuffer, + image::arrangement::Vertical, + pixelcolor::{raw::storage::Msb0, BinaryColor}, +}; use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin}; use error::Error; use mode::{BufferedGraphicsMode, TerminalMode}; use rotation::DisplayRotation; use size::DisplaySize; +type Ssd1306Framebuffer = + Framebuffer, Vertical, WIDTH, HEIGHT, N>; + /// SSD1306 driver. /// /// Note that some methods are only available when the display is configured in a certain [`mode`]. diff --git a/src/mode/buffered_graphics.rs b/src/mode/buffered_graphics.rs index f86dc91e..ca6f1626 100644 --- a/src/mode/buffered_graphics.rs +++ b/src/mode/buffered_graphics.rs @@ -1,12 +1,16 @@ //! Buffered graphics mode. use crate::{ - command::AddrMode, - rotation::DisplayRotation, - size::{DisplaySize, NewZeroed}, - Ssd1306, + command::AddrMode, mode::DisplayConfig, rotation::DisplayRotation, size::DisplaySize, Ssd1306, + Ssd1306Framebuffer, }; use display_interface::{DisplayError, WriteOnlyDataCommand}; +use embedded_graphics::{ + draw_target::DrawTarget, + geometry::{Dimensions, OriginDimensions, Point, Size}, + pixelcolor::BinaryColor, + Pixel, +}; /// Buffered graphics mode. /// @@ -33,7 +37,7 @@ where /// Create a new buffered graphics mode instance. pub(crate) fn new() -> Self { Self { - buffer: NewZeroed::new_zeroed(), + buffer: SIZE::new_buffer(), min_x: 255, max_x: 0, min_y: 255, @@ -42,10 +46,11 @@ where } } -impl DisplayConfig for Ssd1306> +impl DisplayConfig + for Ssd1306> where DI: WriteOnlyDataCommand, - SIZE: DisplaySize, + SIZE: DisplaySize>, { type Error = DisplayError; @@ -63,16 +68,15 @@ where } } -impl Ssd1306> +impl + Ssd1306> where DI: WriteOnlyDataCommand, - SIZE: DisplaySize, + SIZE: DisplaySize>, { /// Clear the display buffer. You need to call `disp.flush()` for any effect on the screen pub fn clear(&mut self) { - for b in self.mode.buffer.as_mut() { - *b = 0; - } + let _ = self.mode.buffer.clear(BinaryColor::Off); let (width, height) = self.dimensions(); self.mode.min_x = 0; @@ -131,7 +135,7 @@ where Self::flush_buffer_chunks( &mut self.interface, - self.mode.buffer.as_mut(), + self.mode.buffer.data(), width as usize, (disp_min_x, disp_min_y), (disp_max_x, disp_max_y), @@ -145,7 +149,7 @@ where Self::flush_buffer_chunks( &mut self.interface, - self.mode.buffer.as_mut(), + self.mode.buffer.data(), height as usize, (disp_min_y, disp_min_x), (disp_max_y, disp_max_x), @@ -157,55 +161,29 @@ where /// Turn a pixel on or off. A non-zero `value` is treated as on, `0` as off. If the X and Y /// coordinates are out of the bounds of the display, this method call is a noop. pub fn set_pixel(&mut self, x: u32, y: u32, value: bool) { - let value = value as u8; - let rotation = self.rotation; - - let (idx, bit) = match rotation { - DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => { - let idx = ((y as usize) / 8 * SIZE::WIDTH as usize) + (x as usize); - let bit = y % 8; - - (idx, bit) - } + let pos = match self.rotation { + DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Point::new(x as i32, y as i32), DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => { - let idx = ((x as usize) / 8 * SIZE::WIDTH as usize) + (y as usize); - let bit = x % 8; - - (idx, bit) + Point::new(y as i32, x as i32) } }; - if let Some(byte) = self.mode.buffer.as_mut().get_mut(idx) { - // Keep track of max and min values - self.mode.min_x = self.mode.min_x.min(x as u8); - self.mode.max_x = self.mode.max_x.max(x as u8); + self.mode.buffer.set_pixel(pos, BinaryColor::from(value)); - self.mode.min_y = self.mode.min_y.min(y as u8); - self.mode.max_y = self.mode.max_y.max(y as u8); + // Keep track of max and min values + self.mode.min_x = self.mode.min_x.min(x as u8); + self.mode.max_x = self.mode.max_x.max(x as u8); - // Set pixel value in byte - // Ref this comment https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit#comment46654671_47990 - *byte = *byte & !(1 << bit) | (value << bit) - } + self.mode.min_y = self.mode.min_y.min(y as u8); + self.mode.max_y = self.mode.max_y.max(y as u8); } } -#[cfg(feature = "graphics")] -use embedded_graphics_core::{ - draw_target::DrawTarget, - geometry::Size, - geometry::{Dimensions, OriginDimensions}, - pixelcolor::BinaryColor, - Pixel, -}; - -use super::DisplayConfig; - -#[cfg(feature = "graphics")] -impl DrawTarget for Ssd1306> +impl DrawTarget + for Ssd1306> where DI: WriteOnlyDataCommand, - SIZE: DisplaySize, + SIZE: DisplaySize>, { type Color = BinaryColor; type Error = DisplayError; @@ -227,7 +205,6 @@ where } } -#[cfg(feature = "graphics")] impl OriginDimensions for Ssd1306> where DI: WriteOnlyDataCommand, diff --git a/src/size.rs b/src/size.rs index ecec0a08..95d28a55 100644 --- a/src/size.rs +++ b/src/size.rs @@ -1,7 +1,9 @@ //! Display size. use super::command::Command; +use crate::Ssd1306Framebuffer; use display_interface::{DisplayError, WriteOnlyDataCommand}; +use embedded_graphics::{framebuffer::buffer_size, pixelcolor::BinaryColor}; /// Workaround trait, since `Default` is only implemented to arrays up to 32 of size pub trait NewZeroed { @@ -38,9 +40,8 @@ pub trait DisplaySize { /// Vertical offset in pixels const OFFSETY: u8 = 0; - /// Size of framebuffer. Because the display is monocrome, this is - /// width * height / 8 - type Buffer: AsMut<[u8]> + NewZeroed; + /// Pixel data frame buffer. + type Buffer; /// Send resolution and model-dependent configuration to the display /// @@ -48,6 +49,9 @@ pub trait DisplaySize { /// and [`Command::InternalIref`](crate::Command::InternalIref) /// for more information fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError>; + + /// Create a new instance of [`DisplaySize::Buffer`]. + fn new_buffer() -> Self::Buffer; } /// Size information for the common 128x64 variants @@ -56,11 +60,20 @@ pub struct DisplaySize128x64; impl DisplaySize for DisplaySize128x64 { const WIDTH: u8 = 128; const HEIGHT: u8 = 64; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(true, false).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } } /// Size information for the common 128x32 variants @@ -69,11 +82,20 @@ pub struct DisplaySize128x32; impl DisplaySize for DisplaySize128x32 { const WIDTH: u8 = 128; const HEIGHT: u8 = 32; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(false, false).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } } /// Size information for the common 96x16 variants @@ -82,11 +104,20 @@ pub struct DisplaySize96x16; impl DisplaySize for DisplaySize96x16 { const WIDTH: u8 = 96; const HEIGHT: u8 = 16; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(false, false).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } } /// Size information for the common 72x40 variants @@ -97,12 +128,21 @@ impl DisplaySize for DisplaySize72x40 { const HEIGHT: u8 = 40; const OFFSETX: u8 = 28; const OFFSETY: u8 = 0; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(true, false).send(iface)?; Command::InternalIref(true, true).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } } /// Size information for the common 64x48 variants @@ -113,11 +153,20 @@ impl DisplaySize for DisplaySize64x48 { const HEIGHT: u8 = 48; const OFFSETX: u8 = 32; const OFFSETY: u8 = 0; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(true, false).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } } /// Size information for the common 64x32 variants @@ -128,9 +177,18 @@ impl DisplaySize for DisplaySize64x32 { const HEIGHT: u8 = 32; const OFFSETX: u8 = 32; const OFFSETY: u8 = 0; - type Buffer = [u8; Self::WIDTH as usize * Self::HEIGHT as usize / 8]; + + type Buffer = Ssd1306Framebuffer< + { Self::WIDTH as usize }, + { Self::HEIGHT as usize }, + { buffer_size::(Self::WIDTH as usize, Self::HEIGHT as usize) }, + >; fn configure(&self, iface: &mut impl WriteOnlyDataCommand) -> Result<(), DisplayError> { Command::ComPinConfig(true, false).send(iface) } + + fn new_buffer() -> Self::Buffer { + Self::Buffer::new() + } }