Skip to content

Commit 49cfa72

Browse files
committed
split display implementation by modes via generics
1 parent e589641 commit 49cfa72

File tree

5 files changed

+171
-68
lines changed

5 files changed

+171
-68
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ embedded-graphics-core = { version = "0.3.3", optional = true }
1818
display-interface = "0.4.1"
1919
display-interface-i2c = "0.4.0"
2020

21+
[dev-dependencies]
22+
embedded-graphics = "0.7.1"
23+
2124
[features]
2225
default = ["graphics"]
2326
graphics = ["embedded-graphics-core"]

src/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub(crate) const DISPLAY_WIDTH: u8 = 128;
22
pub(crate) const DISPLAY_HEIGHT: u8 = 64;
3+
pub(crate) const BUFFER_SIZE: usize = (DISPLAY_WIDTH as usize) * (DISPLAY_HEIGHT as usize) / 8;

src/display.rs

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,100 @@
33
use crate::{command::*, consts::*};
44
use display_interface::{DisplayError, WriteOnlyDataCommand};
55

6-
const BUFFER_SIZE: usize = (DISPLAY_WIDTH as usize) * (DISPLAY_HEIGHT as usize) / 8;
7-
8-
/// ST7565S display driver\
6+
/// ST7565S display driver
97
///
10-
/// - Provides two display modes:
11-
/// - Internal Buffer Mode: This mode allows you to modify an internal buffer by using methods like [`set_pixel`], [`clear`], or by using the [`embedded-graphics`] crate. Once you have made your changes, you can call the [`flush`] method to write the buffer to the display.
12-
/// - Direct Write Mode: This mode allows you to write directly to the display memory by calling the [`draw`] method.
8+
/// Works in two modes:
9+
/// - Direct Write Mode (by default): This mode allows you to write directly to the display memory by calling the [`draw`] method.
10+
/// - Buffered Mode: This mode allows you to modify an internal buffer by using methods like [`set_pixel`], [`clear`], or by using the [`embedded-graphics`] crate. Once you have made your changes, you can call the [`flush`] method to write the buffer to the display.
1311
///
1412
/// [`embedded-graphics`]: https://docs.rs/embedded-graphics
1513
/// [`set_pixel`]: crate::display::ST7567S#method.set_pixel
1614
/// [`clear`]: crate::display::ST7567S#method.clear
1715
/// [`flush`]: crate::display::ST7567S#method.flush
1816
/// [`draw`]: crate::display::ST7567S#method.draw
1917
///
20-
pub struct ST7567S<DI> {
21-
display_interface: DI,
18+
pub struct ST7567S<DI, MODE> {
19+
pub(crate) mode: MODE,
20+
pub(crate) display_interface: DI,
21+
}
22+
23+
/// Basic mode allowing only direct write to the screen controller memory
24+
pub struct DirectWriteMode;
25+
26+
/// Buffered mode allowing to collect changes and then flush them
27+
pub struct BufferedMode {
2228
buffer: [u8; BUFFER_SIZE],
2329
}
2430

25-
impl<DI: WriteOnlyDataCommand> ST7567S<DI> {
26-
/// Create new instance of ST7565S driver\
31+
impl BufferedMode {
32+
pub(crate) fn new() -> Self {
33+
BufferedMode {
34+
buffer: [0; BUFFER_SIZE],
35+
}
36+
}
37+
}
38+
39+
impl<DI: WriteOnlyDataCommand> ST7567S<DI, DirectWriteMode> {
40+
/// Create new instance of ST7565S driver in DirectWriteMode
41+
///
2742
/// # Arguments
2843
/// * `display_interface` - The interface abstraction from `display_interface` crate
2944
pub fn new(display_interface: DI) -> Self {
3045
ST7567S {
46+
mode: DirectWriteMode,
3147
display_interface,
32-
buffer: [0; BUFFER_SIZE],
3348
}
3449
}
3550

51+
/// Move driver to buffered mode
52+
pub fn into_buffered_graphics_mode(self) -> ST7567S<DI, BufferedMode> {
53+
ST7567S {
54+
mode: BufferedMode::new(),
55+
display_interface: self.display_interface,
56+
}
57+
}
58+
}
59+
60+
impl<DI: WriteOnlyDataCommand> ST7567S<DI, BufferedMode> {
61+
/// Clear internal buffer
62+
pub fn clear(&mut self) {
63+
self.mode.buffer = [0; BUFFER_SIZE];
64+
}
65+
66+
/// Set pixel in internal buffer
67+
///
68+
/// Pixel coordinates starts from top left corner and goes to bottom right corner
69+
pub fn set_pixel(&mut self, x: u8, y: u8, value: bool) -> Result<(), DisplayError> {
70+
if x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT {
71+
return Err(DisplayError::OutOfBoundsError);
72+
}
73+
74+
let column: usize = x as usize;
75+
let page: usize = (y / 8) as usize;
76+
let page_bit = y % 8;
77+
78+
let byte_idx = page * (DISPLAY_WIDTH as usize) + column;
79+
let byte = self.mode.buffer[byte_idx];
80+
let bit_value: u8 = value.into();
81+
let byte = byte & !(1 << page_bit) | (bit_value << page_bit);
82+
83+
self.mode.buffer[byte_idx] = byte;
84+
85+
Ok(())
86+
}
87+
88+
/// Send internal buffer to the display
89+
pub fn flush(&mut self) -> Result<(), DisplayError> {
90+
Self::flush_buffer_chunks(
91+
&mut self.display_interface,
92+
self.mode.buffer.as_slice(),
93+
(0, 0),
94+
(DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1),
95+
)
96+
}
97+
}
98+
99+
impl<DI: WriteOnlyDataCommand, MODE> ST7567S<DI, MODE> {
36100
/// Send init commands to the display and turn it on
37101
pub fn init(&mut self) -> Result<(), DisplayError> {
38102
SetBiasCommand::Bias1_9.write(&mut self.display_interface)?;
@@ -49,47 +113,21 @@ impl<DI: WriteOnlyDataCommand> ST7567S<DI> {
49113
.unwrap()
50114
.write(&mut self.display_interface)?;
51115

52-
self.clear();
53-
self.flush()?;
116+
self.draw([0; BUFFER_SIZE].as_slice())?;
54117

55118
DisplayOnCommand::On.write(&mut self.display_interface)?;
56119

57120
Ok(())
58121
}
59122

60-
/// Reset some display parameters to default values: Start Line, Column Address, Page Address and COM Direction\
123+
/// Reset some display parameters to default values: Start Line, Column Address, Page Address and COM Direction.
61124
/// Usually doesn't need to be called
62125
pub fn reset(&mut self) -> Result<(), DisplayError> {
63126
ResetCommand.write(&mut self.display_interface)
64127
}
65128

66-
/// Clear the display buffer
67-
pub fn clear(&mut self) {
68-
self.buffer = [0; BUFFER_SIZE];
69-
}
70-
71-
/// Set pixel in the display buffer\
72-
/// Pixel coordinates starts from top left corner and goes to bottom right corner
73-
pub fn set_pixel(&mut self, x: u8, y: u8, value: bool) -> Result<(), DisplayError> {
74-
if x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT {
75-
return Err(DisplayError::OutOfBoundsError);
76-
}
77-
78-
let column: usize = x as usize;
79-
let page: usize = (y / 8) as usize;
80-
let page_bit = y % 8;
81-
82-
let byte_idx = page * (DISPLAY_WIDTH as usize) + column;
83-
let byte = self.buffer[byte_idx];
84-
let bit_value: u8 = value.into();
85-
let byte = byte & !(1 << page_bit) | (bit_value << page_bit);
86-
87-
self.buffer[byte_idx] = byte;
88-
89-
Ok(())
90-
}
91-
92-
/// Send buffer to the display\
129+
/// Send buffer to the display
130+
///
93131
/// Buffer represents by 8 pages of 128 columns where 1 byte represents 8 vertical pixels
94132
pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
95133
if buffer.len() != BUFFER_SIZE {
@@ -104,7 +142,7 @@ impl<DI: WriteOnlyDataCommand> ST7567S<DI> {
104142
)
105143
}
106144

107-
/// Send part of the buffer to the display\
145+
/// Send part of the buffer to the display.
108146
/// Buffer represents by 8 pages of 128 columns where 1 byte represents 8 vertical pixels
109147
///
110148
/// # Arguments
@@ -119,17 +157,7 @@ impl<DI: WriteOnlyDataCommand> ST7567S<DI> {
119157
Self::flush_buffer_chunks(&mut self.display_interface, buffer, top_left, bottom_right)
120158
}
121159

122-
/// Send internal buffer to the display
123-
pub fn flush(&mut self) -> Result<(), DisplayError> {
124-
Self::flush_buffer_chunks(
125-
&mut self.display_interface,
126-
self.buffer.as_slice(),
127-
(0, 0),
128-
(DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1),
129-
)
130-
}
131-
132-
fn flush_buffer_chunks(
160+
pub(crate) fn flush_buffer_chunks(
133161
display_interface: &mut DI,
134162
buffer: &[u8],
135163
top_left: (u8, u8),

src/graphics.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
//! [`embedded-graphics`](https://docs.rs/embedded-graphics) support
22
3-
use crate::{consts::*, display::ST7567S};
3+
use crate::{
4+
consts::*,
5+
display::{BufferedMode, ST7567S},
6+
};
47
use display_interface::{DisplayError, WriteOnlyDataCommand};
58
use embedded_graphics_core::{
69
draw_target::DrawTarget,
710
pixelcolor::BinaryColor,
8-
prelude::{OriginDimensions, Size},
11+
prelude::{Dimensions, OriginDimensions, Size},
912
Pixel,
1013
};
1114

12-
impl<DI: WriteOnlyDataCommand> OriginDimensions for ST7567S<DI> {
15+
impl<DI: WriteOnlyDataCommand> OriginDimensions for ST7567S<DI, BufferedMode> {
1316
fn size(&self) -> Size {
1417
Size::new(DISPLAY_WIDTH.into(), DISPLAY_HEIGHT.into())
1518
}
1619
}
1720

18-
impl<DI: WriteOnlyDataCommand> DrawTarget for ST7567S<DI> {
21+
impl<DI: WriteOnlyDataCommand> DrawTarget for ST7567S<DI, BufferedMode> {
1922
type Color = BinaryColor;
2023

2124
type Error = DisplayError;
@@ -24,15 +27,11 @@ impl<DI: WriteOnlyDataCommand> DrawTarget for ST7567S<DI> {
2427
where
2528
I: IntoIterator<Item = Pixel<Self::Color>>,
2629
{
30+
let bounding_box = self.bounding_box();
2731
for Pixel(point, color) in pixels.into_iter() {
28-
if point.x < 0 || point.y < 0 {
29-
continue;
30-
}
31-
if point.x >= DISPLAY_WIDTH as i32 || point.y >= DISPLAY_HEIGHT as i32 {
32-
continue;
32+
if bounding_box.contains(point) {
33+
self.set_pixel(point.x as u8, point.y as u8, color.is_on())?;
3334
}
34-
35-
self.set_pixel(point.x as u8, point.y as u8, color.is_on())?;
3635
}
3736

3837
Ok(())

src/lib.rs

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,88 @@
66
//!
77
//! - Supports I2C and SPI communication protocols via the [`display_interface`](https://docs.rs/display_interface) crate.
88
//! - Provides two display modes:
9-
//! - Internal Buffer Mode: This mode allows you to modify an internal buffer by using methods like [`set_pixel`], [`clear`], or by using the [`embedded-graphics`] crate. Once you have made your changes, you can call the [`flush`] method to write the buffer to the display.
10-
//! - Direct Write Mode: This mode allows you to write directly to the display memory by calling the [`draw`] method.
9+
//! - Direct Write Mode (by default): This mode allows you to write directly to the display memory by calling the [`draw`] method.
10+
//! - Buffered Mode: This mode allows you to modify an internal buffer by using methods like [`set_pixel`], [`clear`], or by using the [`embedded-graphics`] crate. Once you have made your changes, you can call the [`flush`] method to write the buffer to the display.
1111
//!
1212
//! [`embedded-graphics`]: https://docs.rs/embedded-graphics
1313
//! [`set_pixel`]: crate::display::ST7567S#method.set_pixel
1414
//! [`clear`]: crate::display::ST7567S#method.clear
1515
//! [`flush`]: crate::display::ST7567S#method.flush
1616
//! [`draw`]: crate::display::ST7567S#method.draw
1717
//!
18-
//! **Note**: This driver is designed to work with a more generic 128x64 resolution, instead of the original 132x65 resolution of the ST7567S controller.
19-
//! **Note**: SPI communication is not tested yet.
18+
//! # Notes
19+
//! - This driver is designed to work with a more common 128x64 resolution, instead of the original 132x65 resolution of the ST7567S controller.
20+
//! - SPI communication is not tested yet.
21+
//!
22+
//! # Examples
23+
//!
24+
//! ### Direct write mode
25+
//! ```rust
26+
//! use st7567s::{
27+
//! display::{DirectWriteMode, ST7567S},
28+
//! interface::{I2CDisplayInterface, I2CInterface},
29+
//! };
30+
//! struct I2CStub;
31+
//! impl embedded_hal::blocking::i2c::Write for I2CStub {
32+
//! type Error = ();
33+
//! fn write(&mut self, _addr: u8, _buf: &[u8]) -> Result<(), ()> {
34+
//! Ok(())
35+
//! }
36+
//! }
37+
//!
38+
//! let i2c = I2CStub;
39+
//! let interface = I2CDisplayInterface::new(i2c);
40+
//! let mut display = ST7567S::new(interface);
41+
//! display.init().unwrap();
42+
//!
43+
//! // Set all pixels to enabled state
44+
//! display
45+
//! .draw([0xff; 128 * 64 / 8].as_slice())
46+
//! .unwrap();
47+
//!
48+
//! ```
49+
//!
50+
//! ### Buffered mode + embedded_graphics
51+
//! ```rust
52+
//! use st7567s::{
53+
//! display::{BufferedMode, ST7567S},
54+
//! interface::{I2CDisplayInterface, I2CInterface},
55+
//! };
56+
//! use embedded_graphics::{
57+
//! mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
58+
//! pixelcolor::BinaryColor,
59+
//! prelude::*,
60+
//! text::{Baseline, Text},
61+
//! };
62+
//! struct I2CStub;
63+
//! impl embedded_hal::blocking::i2c::Write for I2CStub {
64+
//! type Error = ();
65+
//! fn write(&mut self, _addr: u8, _buf: &[u8]) -> Result<(), ()> {
66+
//! Ok(())
67+
//! }
68+
//! }
69+
//!
70+
//! let i2c = I2CStub;
71+
//! let interface = I2CDisplayInterface::new(i2c);
72+
//! let mut display = ST7567S::new(interface)
73+
//! .into_buffered_graphics_mode();
74+
//! display.init().unwrap();
75+
//!
76+
//! let text_style = MonoTextStyleBuilder::new()
77+
//! .font(&FONT_6X10)
78+
//! .text_color(BinaryColor::On)
79+
//! .build();
80+
//!
81+
//! Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top)
82+
//! .draw(&mut display)
83+
//! .unwrap();
84+
//!
85+
//! Text::with_baseline("Hello Rust!", Point::new(0, 16), text_style, Baseline::Top)
86+
//! .draw(&mut display)
87+
//! .unwrap();
88+
//!
89+
//! display.flush().unwrap();
90+
//! ```
2091
2192
#![no_std]
2293

@@ -26,3 +97,4 @@ pub mod display;
2697
#[cfg(feature = "graphics")]
2798
pub mod graphics;
2899
pub mod interface;
100+
pub mod prelude;

0 commit comments

Comments
 (0)