Skip to content

Commit

Permalink
--wip-- [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
jamwaffles committed Jun 25, 2022
1 parent 1b1d8cd commit e640e1b
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 130 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ circle-ci = { repository = "jamwaffles/sh1106", branch = "master" }
[dependencies]
embedded-hal = "0.2.3"
embedded-graphics-core = { version = "0.3.2", optional = true }
# packed-display-buffer = { git = "https://github.com/embedded-graphics/packed-display-buffer.git", rev = "3741342d43c78fc8984db6ea316e7b13270ec4b7" }
packed-display-buffer = { path = "../embedded-graphics/packed-display-buffer" }

[dev-dependencies]
cortex-m = "0.7.3"
Expand All @@ -44,5 +46,5 @@ incremental = false

[profile.release]
codegen-units = 1
debug = true
debug = 2
lto = true
6 changes: 4 additions & 2 deletions examples/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use embedded_graphics::{
primitives::{Circle, Line, PrimitiveStyle, Rectangle},
};
use panic_semihosting as _;
use sh1106::{prelude::*, Builder};
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
use stm32f1xx_hal::{
i2c::{BlockingI2c, DutyCycle, Mode},
prelude::*,
Expand Down Expand Up @@ -66,7 +66,9 @@ fn main() -> ! {
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();

display.init().unwrap();
display.flush().unwrap();
display.clear(Clear::BufferAndDisplay).unwrap();

display.set_rotation(DisplayRotation::Rotate90).unwrap();

Line::new(Point::new(8, 16 + 16), Point::new(8 + 16, 16 + 16))
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))
Expand Down
4 changes: 2 additions & 2 deletions examples/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use embedded_graphics::{
prelude::*,
};
use panic_semihosting as _;
use sh1106::{prelude::*, Builder};
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
use stm32f1xx_hal::{
i2c::{BlockingI2c, DutyCycle, Mode},
prelude::*,
Expand Down Expand Up @@ -76,7 +76,7 @@ fn main() -> ! {
.into();

display.init().unwrap();
display.flush().unwrap();
display.clear(Clear::BufferAndDisplay).unwrap();

// Contrived example to test builder and instance methods. Sets rotation to 270 degress
// or 90 degress counterclockwise
Expand Down
6 changes: 3 additions & 3 deletions examples/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use embedded_graphics::{
text::{Baseline, Text},
};
use panic_semihosting as _;
use sh1106::{prelude::*, Builder};
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
use stm32f1xx_hal::{
i2c::{BlockingI2c, DutyCycle, Mode},
prelude::*,
Expand Down Expand Up @@ -68,7 +68,7 @@ fn main() -> ! {
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();

display.init().unwrap();
display.flush().unwrap();
display.clear(Clear::BufferAndDisplay).unwrap();

let text_style = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
Expand All @@ -79,7 +79,7 @@ fn main() -> ! {
.draw(&mut display)
.unwrap();

Text::with_baseline("Hello Rust!", Point::new(0, 16), text_style, Baseline::Top)
Text::with_baseline("Hello Rust!", Point::new(0, 10), text_style, Baseline::Top)
.draw(&mut display)
.unwrap();

Expand Down
48 changes: 26 additions & 22 deletions src/interface/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use hal;

use super::DisplayInterface;
use crate::{command::Page, Error};
use crate::Error;

/// SH1106 I2C communication interface
pub struct I2cInterface<I2C> {
Expand Down Expand Up @@ -52,33 +52,37 @@ where
return Ok(());
}

let mut page = Page::Page0 as u8;
// let mut page = Page::Page0 as u8;

// Display width plus 4 start bytes
let mut writebuf: [u8; BUFLEN] = [0; BUFLEN];

writebuf[0] = 0x40; // Following bytes are data bytes

for chunk in buf.chunks(CHUNKLEN) {
// Copy over all data from buffer, leaving the data command byte intact
writebuf[1..BUFLEN].copy_from_slice(&chunk);

self.i2c
.write(
self.addr,
&[
0x00, // Command
page, // Page address
0x02, // Lower column address
0x10, // Upper column address (always zero, base is 10h)
],
)
.map_err(Error::Comm)?;

self.i2c.write(self.addr, &writebuf).map_err(Error::Comm)?;

page += 1;
}
// for chunk in buf.chunks(CHUNKLEN) {
let sub_buf = &mut writebuf[1..(buf.len() + 1)];

// Copy over all data from buffer, leaving the data command byte intact
sub_buf.copy_from_slice(&buf);

let send_buf = &writebuf[0..(buf.len() + 1)];

// self.i2c
// .write(
// self.addr,
// &[
// 0x00, // Command
// page, // Page address
// 0x02, // Lower column address
// 0x10, // Upper column address (always zero, base is 10h)
// ],
// )
// .map_err(Error::Comm)?;

self.i2c.write(self.addr, &send_buf).map_err(Error::Comm)?;

// page += 1;
// }

Ok(())
}
Expand Down
158 changes: 62 additions & 96 deletions src/mode/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,33 @@
//! ```

use crate::{
displayrotation::DisplayRotation, interface::DisplayInterface,
command::Page, displayrotation::DisplayRotation, interface::DisplayInterface,
mode::displaymode::DisplayModeTrait, properties::DisplayProperties, Error,
};
use embedded_graphics_core::{prelude::Point, primitives::Rectangle};
use hal::{blocking::delay::DelayMs, digital::v2::OutputPin};

const BUFFER_SIZE: usize = 132 * 64 / 8;
/// What to clear.
#[derive(Debug, Copy, Clone)]
pub enum Clear {
/// Clear the display buffer only, leaving the display contents alone.
Buffer,

/// Clear both the buffer and display.
BufferAndDisplay,
}

// const BUFFER_SIZE: usize = 132 * 64 / 8;
const W: u32 = 132;
const H: u32 = 64;

/// Graphics mode handler
pub struct GraphicsMode<DI>
where
DI: DisplayInterface,
{
properties: DisplayProperties<DI>,
buffer: [u8; BUFFER_SIZE],
buffer: PackedBuffer<W, H, { (W * H / 8) as usize }>,
}

impl<DI> DisplayModeTrait<DI> for GraphicsMode<DI>
Expand All @@ -68,7 +80,7 @@ where
fn new(properties: DisplayProperties<DI>) -> Self {
GraphicsMode {
properties,
buffer: [0; BUFFER_SIZE],
buffer: PackedBuffer::new(),
}
}

Expand All @@ -82,9 +94,21 @@ impl<DI> GraphicsMode<DI>
where
DI: DisplayInterface,
{
/// Clear the display buffer. You need to call `display.flush()` for any effect on the screen
pub fn clear(&mut self) {
self.buffer = [0; BUFFER_SIZE];
/// Clear the display buffer.
pub fn clear(&mut self, clear: Clear) -> Result<(), DI::Error> {
self.buffer = PackedBuffer::new();

if matches!(clear, Clear::BufferAndDisplay) {
let display_size = self.properties.get_size();
let column_offset = display_size.column_offset();

for i in 0..8 {
self.properties
.draw_page(Page::from(i * 8), column_offset, &[0x00; 128])?;
}
}

Ok(())
}

/// Reset display
Expand All @@ -104,72 +128,46 @@ where
rst.set_high().map_err(Error::Pin)
}

/// Write out data to display
/// Write out data to display.
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
// to prevent accidental offsets
let (display_width, display_height) = display_size.dimensions();
let active = self.buffer.active_area().intersection(&self.bounding_box());
let start_page = (active.top_left.y / 8) as u8;
let start_column = active.top_left.x as u8;

let column_offset = display_size.column_offset();
self.properties.set_draw_area(
(column_offset, 0),
(display_width + column_offset, display_height),
)?;

let length = (display_width as usize) * (display_height as usize) / 8;
for (i, block) in self.buffer.active_blocks().enumerate() {
let page = Page::from((start_page + i as u8) * 8);

self.properties
.draw_page(page, column_offset + start_column, block)?;
}

self.properties.draw(&self.buffer[..length])
Ok(())
}

/// 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: u8) {
let (display_width, _) = self.properties.get_size().dimensions();
let display_rotation = self.properties.get_rotation();

let idx = match display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
if x >= display_width as u32 {
return;
}
((y as usize) / 8 * display_width as usize) + (x as usize)
}

let point = match display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Point::new(x as i32, y as i32),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
if y >= display_width as u32 {
return;
}
((x as usize) / 8 * display_width as usize) + (y as usize)
Point::new(y as i32, x as i32)
}
};

if idx >= self.buffer.len() {
return;
}

let (byte, bit) = match display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
let byte =
&mut self.buffer[((y as usize) / 8 * display_width as usize) + (x as usize)];
let bit = 1 << (y % 8);

(byte, bit)
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
let byte =
&mut self.buffer[((x as usize) / 8 * display_width as usize) + (y as usize)];
let bit = 1 << (x % 8);

(byte, bit)
}
};

if value == 0 {
*byte &= !bit;
} else {
*byte |= bit;
}
self.buffer.set_pixel(
point,
if value == 0 {
BinaryColor::Off
} else {
BinaryColor::On
},
)
}

/// Display is set up in column mode, i.e. a byte walks down a column of 8 pixels from
Expand Down Expand Up @@ -202,6 +200,7 @@ use embedded_graphics_core::{
pixelcolor::BinaryColor,
Pixel,
};
use packed_display_buffer::PackedBuffer;

#[cfg(feature = "graphics")]
impl<DI> DrawTarget for GraphicsMode<DI>
Expand All @@ -228,47 +227,14 @@ where
}

fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
let Rectangle {
top_left: Point { x, y },
size: Size { width, height },
} = area.intersection(&self.bounding_box());
// swap coordinates if rotated
let (x, y, width, height) = match self.properties.get_rotation() {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (x, y, width, height),
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (y, x, height, width),
};

let color = if color.is_on() { 0xff } else { 0x00 };

let display_width = self.properties.get_size().dimensions().0 as u32;

// Display is at most 128 pixels tall when rotated by 90º or 270º so we'll use a u128 here
let fill = 2u128.pow(height) - 1;
let moved = fill << y;

let start_block = (y / 8) as usize;

// Represents a bit mask of a single column of the entire display height
let whole_column = moved.to_le_bytes();

let end_block = start_block + (height as usize / 8 + 1);

// Ensure we don't wrap off the bottom of the screen
let end_block = end_block.min(7);

for current_x in x..(x + width as i32) {
for block in start_block..=end_block {
let buffer_offset = current_x as usize + display_width as usize * block;

let current = self.buffer[buffer_offset];

let mask = whole_column[block];

self.buffer[buffer_offset] = (current & !mask) | (color & mask);
}
}
self.buffer.fill_solid(area, color)
}

Ok(())
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Self::Color>,
{
self.buffer.fill_contiguous(area, colors)
}
}

Expand Down
Loading

0 comments on commit e640e1b

Please sign in to comment.