Skip to content

Commit e640e1b

Browse files
committed
--wip-- [skip ci]
1 parent 1b1d8cd commit e640e1b

File tree

7 files changed

+118
-130
lines changed

7 files changed

+118
-130
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ circle-ci = { repository = "jamwaffles/sh1106", branch = "master" }
2121
[dependencies]
2222
embedded-hal = "0.2.3"
2323
embedded-graphics-core = { version = "0.3.2", optional = true }
24+
# packed-display-buffer = { git = "https://github.com/embedded-graphics/packed-display-buffer.git", rev = "3741342d43c78fc8984db6ea316e7b13270ec4b7" }
25+
packed-display-buffer = { path = "../embedded-graphics/packed-display-buffer" }
2426

2527
[dev-dependencies]
2628
cortex-m = "0.7.3"
@@ -44,5 +46,5 @@ incremental = false
4446

4547
[profile.release]
4648
codegen-units = 1
47-
debug = true
49+
debug = 2
4850
lto = true

examples/graphics.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use embedded_graphics::{
2424
primitives::{Circle, Line, PrimitiveStyle, Rectangle},
2525
};
2626
use panic_semihosting as _;
27-
use sh1106::{prelude::*, Builder};
27+
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
2828
use stm32f1xx_hal::{
2929
i2c::{BlockingI2c, DutyCycle, Mode},
3030
prelude::*,
@@ -66,7 +66,9 @@ fn main() -> ! {
6666
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
6767

6868
display.init().unwrap();
69-
display.flush().unwrap();
69+
display.clear(Clear::BufferAndDisplay).unwrap();
70+
71+
display.set_rotation(DisplayRotation::Rotate90).unwrap();
7072

7173
Line::new(Point::new(8, 16 + 16), Point::new(8 + 16, 16 + 16))
7274
.into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1))

examples/rotation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use embedded_graphics::{
3030
prelude::*,
3131
};
3232
use panic_semihosting as _;
33-
use sh1106::{prelude::*, Builder};
33+
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
3434
use stm32f1xx_hal::{
3535
i2c::{BlockingI2c, DutyCycle, Mode},
3636
prelude::*,
@@ -76,7 +76,7 @@ fn main() -> ! {
7676
.into();
7777

7878
display.init().unwrap();
79-
display.flush().unwrap();
79+
display.clear(Clear::BufferAndDisplay).unwrap();
8080

8181
// Contrived example to test builder and instance methods. Sets rotation to 270 degress
8282
// or 90 degress counterclockwise

examples/text.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use embedded_graphics::{
2626
text::{Baseline, Text},
2727
};
2828
use panic_semihosting as _;
29-
use sh1106::{prelude::*, Builder};
29+
use sh1106::{mode::graphics::Clear, prelude::*, Builder};
3030
use stm32f1xx_hal::{
3131
i2c::{BlockingI2c, DutyCycle, Mode},
3232
prelude::*,
@@ -68,7 +68,7 @@ fn main() -> ! {
6868
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
6969

7070
display.init().unwrap();
71-
display.flush().unwrap();
71+
display.clear(Clear::BufferAndDisplay).unwrap();
7272

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

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

src/interface/i2c.rs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use hal;
44

55
use super::DisplayInterface;
6-
use crate::{command::Page, Error};
6+
use crate::Error;
77

88
/// SH1106 I2C communication interface
99
pub struct I2cInterface<I2C> {
@@ -52,33 +52,37 @@ where
5252
return Ok(());
5353
}
5454

55-
let mut page = Page::Page0 as u8;
55+
// let mut page = Page::Page0 as u8;
5656

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

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

62-
for chunk in buf.chunks(CHUNKLEN) {
63-
// Copy over all data from buffer, leaving the data command byte intact
64-
writebuf[1..BUFLEN].copy_from_slice(&chunk);
65-
66-
self.i2c
67-
.write(
68-
self.addr,
69-
&[
70-
0x00, // Command
71-
page, // Page address
72-
0x02, // Lower column address
73-
0x10, // Upper column address (always zero, base is 10h)
74-
],
75-
)
76-
.map_err(Error::Comm)?;
77-
78-
self.i2c.write(self.addr, &writebuf).map_err(Error::Comm)?;
79-
80-
page += 1;
81-
}
62+
// for chunk in buf.chunks(CHUNKLEN) {
63+
let sub_buf = &mut writebuf[1..(buf.len() + 1)];
64+
65+
// Copy over all data from buffer, leaving the data command byte intact
66+
sub_buf.copy_from_slice(&buf);
67+
68+
let send_buf = &writebuf[0..(buf.len() + 1)];
69+
70+
// self.i2c
71+
// .write(
72+
// self.addr,
73+
// &[
74+
// 0x00, // Command
75+
// page, // Page address
76+
// 0x02, // Lower column address
77+
// 0x10, // Upper column address (always zero, base is 10h)
78+
// ],
79+
// )
80+
// .map_err(Error::Comm)?;
81+
82+
self.i2c.write(self.addr, &send_buf).map_err(Error::Comm)?;
83+
84+
// page += 1;
85+
// }
8286

8387
Ok(())
8488
}

src/mode/graphics.rs

Lines changed: 62 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,33 @@
4343
//! ```
4444
4545
use crate::{
46-
displayrotation::DisplayRotation, interface::DisplayInterface,
46+
command::Page, displayrotation::DisplayRotation, interface::DisplayInterface,
4747
mode::displaymode::DisplayModeTrait, properties::DisplayProperties, Error,
4848
};
4949
use embedded_graphics_core::{prelude::Point, primitives::Rectangle};
5050
use hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
5151

52-
const BUFFER_SIZE: usize = 132 * 64 / 8;
52+
/// What to clear.
53+
#[derive(Debug, Copy, Clone)]
54+
pub enum Clear {
55+
/// Clear the display buffer only, leaving the display contents alone.
56+
Buffer,
57+
58+
/// Clear both the buffer and display.
59+
BufferAndDisplay,
60+
}
61+
62+
// const BUFFER_SIZE: usize = 132 * 64 / 8;
63+
const W: u32 = 132;
64+
const H: u32 = 64;
5365

5466
/// Graphics mode handler
5567
pub struct GraphicsMode<DI>
5668
where
5769
DI: DisplayInterface,
5870
{
5971
properties: DisplayProperties<DI>,
60-
buffer: [u8; BUFFER_SIZE],
72+
buffer: PackedBuffer<W, H, { (W * H / 8) as usize }>,
6173
}
6274

6375
impl<DI> DisplayModeTrait<DI> for GraphicsMode<DI>
@@ -68,7 +80,7 @@ where
6880
fn new(properties: DisplayProperties<DI>) -> Self {
6981
GraphicsMode {
7082
properties,
71-
buffer: [0; BUFFER_SIZE],
83+
buffer: PackedBuffer::new(),
7284
}
7385
}
7486

@@ -82,9 +94,21 @@ impl<DI> GraphicsMode<DI>
8294
where
8395
DI: DisplayInterface,
8496
{
85-
/// Clear the display buffer. You need to call `display.flush()` for any effect on the screen
86-
pub fn clear(&mut self) {
87-
self.buffer = [0; BUFFER_SIZE];
97+
/// Clear the display buffer.
98+
pub fn clear(&mut self, clear: Clear) -> Result<(), DI::Error> {
99+
self.buffer = PackedBuffer::new();
100+
101+
if matches!(clear, Clear::BufferAndDisplay) {
102+
let display_size = self.properties.get_size();
103+
let column_offset = display_size.column_offset();
104+
105+
for i in 0..8 {
106+
self.properties
107+
.draw_page(Page::from(i * 8), column_offset, &[0x00; 128])?;
108+
}
109+
}
110+
111+
Ok(())
88112
}
89113

90114
/// Reset display
@@ -104,72 +128,46 @@ where
104128
rst.set_high().map_err(Error::Pin)
105129
}
106130

107-
/// Write out data to display
131+
/// Write out data to display.
108132
pub fn flush(&mut self) -> Result<(), DI::Error> {
109133
let display_size = self.properties.get_size();
110134

111-
// Ensure the display buffer is at the origin of the display before we send the full frame
112-
// to prevent accidental offsets
113-
let (display_width, display_height) = display_size.dimensions();
135+
let active = self.buffer.active_area().intersection(&self.bounding_box());
136+
let start_page = (active.top_left.y / 8) as u8;
137+
let start_column = active.top_left.x as u8;
138+
114139
let column_offset = display_size.column_offset();
115-
self.properties.set_draw_area(
116-
(column_offset, 0),
117-
(display_width + column_offset, display_height),
118-
)?;
119140

120-
let length = (display_width as usize) * (display_height as usize) / 8;
141+
for (i, block) in self.buffer.active_blocks().enumerate() {
142+
let page = Page::from((start_page + i as u8) * 8);
143+
144+
self.properties
145+
.draw_page(page, column_offset + start_column, block)?;
146+
}
121147

122-
self.properties.draw(&self.buffer[..length])
148+
Ok(())
123149
}
124150

125151
/// Turn a pixel on or off. A non-zero `value` is treated as on, `0` as off. If the X and Y
126152
/// coordinates are out of the bounds of the display, this method call is a noop.
127153
pub fn set_pixel(&mut self, x: u32, y: u32, value: u8) {
128-
let (display_width, _) = self.properties.get_size().dimensions();
129154
let display_rotation = self.properties.get_rotation();
130155

131-
let idx = match display_rotation {
132-
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
133-
if x >= display_width as u32 {
134-
return;
135-
}
136-
((y as usize) / 8 * display_width as usize) + (x as usize)
137-
}
138-
156+
let point = match display_rotation {
157+
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Point::new(x as i32, y as i32),
139158
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
140-
if y >= display_width as u32 {
141-
return;
142-
}
143-
((x as usize) / 8 * display_width as usize) + (y as usize)
159+
Point::new(y as i32, x as i32)
144160
}
145161
};
146162

147-
if idx >= self.buffer.len() {
148-
return;
149-
}
150-
151-
let (byte, bit) = match display_rotation {
152-
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
153-
let byte =
154-
&mut self.buffer[((y as usize) / 8 * display_width as usize) + (x as usize)];
155-
let bit = 1 << (y % 8);
156-
157-
(byte, bit)
158-
}
159-
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
160-
let byte =
161-
&mut self.buffer[((x as usize) / 8 * display_width as usize) + (y as usize)];
162-
let bit = 1 << (x % 8);
163-
164-
(byte, bit)
165-
}
166-
};
167-
168-
if value == 0 {
169-
*byte &= !bit;
170-
} else {
171-
*byte |= bit;
172-
}
163+
self.buffer.set_pixel(
164+
point,
165+
if value == 0 {
166+
BinaryColor::Off
167+
} else {
168+
BinaryColor::On
169+
},
170+
)
173171
}
174172

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

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

230229
fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
231-
let Rectangle {
232-
top_left: Point { x, y },
233-
size: Size { width, height },
234-
} = area.intersection(&self.bounding_box());
235-
// swap coordinates if rotated
236-
let (x, y, width, height) = match self.properties.get_rotation() {
237-
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (x, y, width, height),
238-
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (y, x, height, width),
239-
};
240-
241-
let color = if color.is_on() { 0xff } else { 0x00 };
242-
243-
let display_width = self.properties.get_size().dimensions().0 as u32;
244-
245-
// Display is at most 128 pixels tall when rotated by 90º or 270º so we'll use a u128 here
246-
let fill = 2u128.pow(height) - 1;
247-
let moved = fill << y;
248-
249-
let start_block = (y / 8) as usize;
250-
251-
// Represents a bit mask of a single column of the entire display height
252-
let whole_column = moved.to_le_bytes();
253-
254-
let end_block = start_block + (height as usize / 8 + 1);
255-
256-
// Ensure we don't wrap off the bottom of the screen
257-
let end_block = end_block.min(7);
258-
259-
for current_x in x..(x + width as i32) {
260-
for block in start_block..=end_block {
261-
let buffer_offset = current_x as usize + display_width as usize * block;
262-
263-
let current = self.buffer[buffer_offset];
264-
265-
let mask = whole_column[block];
266-
267-
self.buffer[buffer_offset] = (current & !mask) | (color & mask);
268-
}
269-
}
230+
self.buffer.fill_solid(area, color)
231+
}
270232

271-
Ok(())
233+
fn fill_contiguous<I>(&mut self, area: &Rectangle, colors: I) -> Result<(), Self::Error>
234+
where
235+
I: IntoIterator<Item = Self::Color>,
236+
{
237+
self.buffer.fill_contiguous(area, colors)
272238
}
273239
}
274240

0 commit comments

Comments
 (0)