st7567s/
display.rs

1//! Display driver
2
3use crate::{command::*, consts::*};
4use display_interface::{DisplayError, WriteOnlyDataCommand};
5
6/// ST7565S display driver
7///
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.
11///
12/// [`embedded-graphics`]: https://docs.rs/embedded-graphics
13/// [`set_pixel`]: crate::display::ST7567S#method.set_pixel
14/// [`clear`]: crate::display::ST7567S#method.clear
15/// [`flush`]: crate::display::ST7567S#method.flush
16/// [`draw`]: crate::display::ST7567S#method.draw
17///
18pub 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
24pub struct DirectWriteMode;
25
26/// Buffered mode allowing to collect changes and then flush them
27pub struct BufferedMode {
28    buffer: [u8; BUFFER_SIZE],
29}
30
31impl BufferedMode {
32    pub(crate) fn new() -> Self {
33        BufferedMode {
34            buffer: [0; BUFFER_SIZE],
35        }
36    }
37}
38
39impl<DI: WriteOnlyDataCommand> ST7567S<DI, DirectWriteMode> {
40    /// Create new instance of ST7565S driver in DirectWriteMode
41    ///
42    /// # Arguments
43    /// * `display_interface` - The interface abstraction from `display_interface` crate
44    pub fn new(display_interface: DI) -> Self {
45        ST7567S {
46            mode: DirectWriteMode,
47            display_interface,
48        }
49    }
50
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
60impl<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
99impl<DI: WriteOnlyDataCommand, MODE> ST7567S<DI, MODE> {
100    /// Send init commands to the display and turn it on
101    pub fn init(&mut self) -> Result<(), DisplayError> {
102        SetBiasCommand::Bias1_9.write(&mut self.display_interface)?;
103        SetSEGDirectionCommand::Normal.write(&mut self.display_interface)?;
104        SetCOMDirectionCommand::Reverse.write(&mut self.display_interface)?;
105        SetRegulationResistorRatioCommand::Ratio5_0.write(&mut self.display_interface)?;
106        SetElectronicVolumeCommand::new(40)
107            .unwrap()
108            .write(&mut self.display_interface)?;
109        SetPowerControlCommand::BoosterOn.write(&mut self.display_interface)?;
110        SetPowerControlCommand::VoltageRegulatorOn.write(&mut self.display_interface)?;
111        SetPowerControlCommand::VoltageFollowerOn.write(&mut self.display_interface)?;
112        SetStartLineCommand::new(0)
113            .unwrap()
114            .write(&mut self.display_interface)?;
115
116        self.draw([0; BUFFER_SIZE].as_slice())?;
117
118        DisplayOnCommand::On.write(&mut self.display_interface)?;
119
120        Ok(())
121    }
122
123    /// Reset some display parameters to default values: Start Line, Column Address, Page Address and COM Direction.
124    /// Usually doesn't need to be called
125    pub fn reset(&mut self) -> Result<(), DisplayError> {
126        ResetCommand.write(&mut self.display_interface)
127    }
128
129    /// Send buffer to the display
130    ///
131    /// Buffer represents by 8 pages of 128 columns where 1 byte represents 8 vertical pixels
132    pub fn draw(&mut self, buffer: &[u8]) -> Result<(), DisplayError> {
133        if buffer.len() != BUFFER_SIZE {
134            return Err(DisplayError::OutOfBoundsError);
135        }
136
137        Self::flush_buffer_chunks(
138            &mut self.display_interface,
139            buffer,
140            (0, 0),
141            (DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1),
142        )
143    }
144
145    /// Send part of the buffer to the display.
146    /// Buffer represents by 8 pages of 128 columns where 1 byte represents 8 vertical pixels
147    ///
148    /// # Arguments
149    /// * `buffer` - the entire buffer from which the required part will be sent
150    /// * `top_left` and `bottom_right` are coordinates of the top left and bottom right corners of the area to be drawn
151    pub fn bounded_draw(
152        &mut self,
153        buffer: &[u8],
154        top_left: (u8, u8),
155        bottom_right: (u8, u8),
156    ) -> Result<(), DisplayError> {
157        Self::flush_buffer_chunks(&mut self.display_interface, buffer, top_left, bottom_right)
158    }
159
160    pub(crate) fn flush_buffer_chunks(
161        display_interface: &mut DI,
162        buffer: &[u8],
163        top_left: (u8, u8),
164        bottom_right: (u8, u8),
165    ) -> Result<(), DisplayError> {
166        if top_left.0 >= DISPLAY_WIDTH || top_left.1 >= DISPLAY_HEIGHT {
167            return Err(DisplayError::OutOfBoundsError);
168        }
169        if bottom_right.0 >= DISPLAY_WIDTH || bottom_right.1 >= DISPLAY_HEIGHT {
170            return Err(DisplayError::OutOfBoundsError);
171        }
172        if top_left.0 > bottom_right.0 || top_left.1 > bottom_right.1 {
173            return Err(DisplayError::OutOfBoundsError);
174        }
175
176        let first_page: usize = (top_left.1 / 8) as usize;
177        let first_column: usize = top_left.0 as usize;
178
179        let last_page: usize = (bottom_right.1 / 8) as usize;
180        let last_column: usize = bottom_right.0 as usize;
181
182        buffer
183            .chunks(DISPLAY_WIDTH as usize)
184            .skip(first_page)
185            .take(last_page - first_page + 1)
186            .map(|page| &page[first_column..=last_column])
187            .enumerate()
188            .try_for_each(|(page_idx, page)| {
189                SetPageAddressCommand::new(first_page as u8 + page_idx as u8)
190                    .unwrap()
191                    .write(display_interface)?;
192                SetColumnAddressLSNibbleCommand::new(first_column as u8)
193                    .unwrap()
194                    .write(display_interface)?;
195                SetColumnAddressMSNibbleCommand::new(first_column as u8)
196                    .unwrap()
197                    .write(display_interface)?;
198
199                display_interface.send_data(display_interface::DataFormat::U8(page))
200            })
201    }
202}