Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

embedded-graphics "0.8" #169

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -50,3 +46,6 @@ incremental = false
codegen-units = 1
debug = true
lto = true

[patch.crates-io]
embedded-graphics = { path = "../embedded-graphics" }
1 change: 1 addition & 0 deletions examples/rotation_i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const WIDTH: usize, const HEIGHT: usize, const N: usize> =
Framebuffer<Msb0<BinaryColor>, Vertical, WIDTH, HEIGHT, N>;

/// SSD1306 driver.
///
/// Note that some methods are only available when the display is configured in a certain [`mode`].
Expand Down
83 changes: 30 additions & 53 deletions src/mode/buffered_graphics.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand All @@ -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,
Expand All @@ -42,10 +46,11 @@ where
}
}

impl<DI, SIZE> DisplayConfig for Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, DI, SIZE> DisplayConfig
for Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
where
DI: WriteOnlyDataCommand,
SIZE: DisplaySize,
SIZE: DisplaySize<Buffer = Ssd1306Framebuffer<WIDTH, HEIGHT, N>>,
{
type Error = DisplayError;

Expand All @@ -63,16 +68,15 @@ where
}
}

impl<DI, SIZE> Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, DI, SIZE>
Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
where
DI: WriteOnlyDataCommand,
SIZE: DisplaySize,
SIZE: DisplaySize<Buffer = Ssd1306Framebuffer<WIDTH, HEIGHT, N>>,
{
/// 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;
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand All @@ -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<DI, SIZE> DrawTarget for Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
impl<const WIDTH: usize, const HEIGHT: usize, const N: usize, DI, SIZE> DrawTarget
for Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
where
DI: WriteOnlyDataCommand,
SIZE: DisplaySize,
SIZE: DisplaySize<Buffer = Ssd1306Framebuffer<WIDTH, HEIGHT, N>>,
{
type Color = BinaryColor;
type Error = DisplayError;
Expand All @@ -227,7 +205,6 @@ where
}
}

#[cfg(feature = "graphics")]
impl<DI, SIZE> OriginDimensions for Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>
where
DI: WriteOnlyDataCommand,
Expand Down
76 changes: 67 additions & 9 deletions src/size.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -38,16 +40,18 @@ 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
///
/// See [`Command::ComPinConfig`](crate::Command::ComPinConfig)
/// 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
Expand All @@ -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::<BinaryColor>(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
Expand All @@ -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::<BinaryColor>(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
Expand All @@ -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::<BinaryColor>(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
Expand All @@ -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::<BinaryColor>(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
Expand All @@ -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::<BinaryColor>(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
Expand All @@ -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::<BinaryColor>(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()
}
}