epd_waveshare/epd2in9/
mod.rs

1//! A simple Driver for the Waveshare 2.9" E-Ink Display via SPI
2//!
3//!
4//! # Example for the 2.9 in E-Ink Display
5//!
6//!```rust, no_run
7//!# use embedded_hal_mock::eh1::*;
8//!# fn main() -> Result<(), embedded_hal::spi::ErrorKind> {
9//!use embedded_graphics::{
10//!    pixelcolor::BinaryColor::On as Black, prelude::*, primitives::{Line, PrimitiveStyle},
11//!};
12//!use epd_waveshare::{epd2in9::*, prelude::*};
13//!#
14//!# let expectations = [];
15//!# let mut spi = spi::Mock::new(&expectations);
16//!# let expectations = [];
17//!# let cs_pin = digital::Mock::new(&expectations);
18//!# let busy_in = digital::Mock::new(&expectations);
19//!# let dc = digital::Mock::new(&expectations);
20//!# let rst = digital::Mock::new(&expectations);
21//!# let mut delay = delay::NoopDelay::new();
22//!
23//!// Setup EPD
24//!let mut epd = Epd2in9::new(&mut spi, busy_in, dc, rst, &mut delay, None)?;
25//!
26//!// Use display graphics from embedded-graphics
27//!let mut display = Display2in9::default();
28//!
29//!// Use embedded graphics for drawing a line
30//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295))
31//!    .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
32//!    .draw(&mut display);
33//!
34//!    // Display updated frame
35//!epd.update_frame(&mut spi, &display.buffer(), &mut delay)?;
36//!epd.display_frame(&mut spi, &mut delay)?;
37//!
38//!// Set the EPD to sleep
39//!epd.sleep(&mut spi, &mut delay)?;
40//!# Ok(())
41//!# }
42//!```
43
44/// Width of epd2in9 in pixels
45pub const WIDTH: u32 = 128;
46/// Height of epd2in9 in pixels
47pub const HEIGHT: u32 = 296;
48/// Default Background Color (white)
49pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
50const IS_BUSY_LOW: bool = false;
51const SINGLE_BYTE_WRITE: bool = true;
52
53use embedded_hal::{delay::*, digital::*, spi::SpiDevice};
54
55use crate::type_a::{
56    command::Command,
57    constants::{LUT_FULL_UPDATE, LUT_PARTIAL_UPDATE},
58};
59
60use crate::color::Color;
61
62use crate::traits::*;
63
64use crate::buffer_len;
65use crate::interface::DisplayInterface;
66
67/// Display with Fullsize buffer for use with the 2in9 EPD
68#[cfg(feature = "graphics")]
69pub type Display2in9 = crate::graphics::Display<
70    WIDTH,
71    HEIGHT,
72    false,
73    { buffer_len(WIDTH as usize, HEIGHT as usize) },
74    Color,
75>;
76
77/// Epd2in9 driver
78///
79pub struct Epd2in9<SPI, BUSY, DC, RST, DELAY> {
80    /// SPI
81    interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
82    /// Color
83    background_color: Color,
84    /// Refresh LUT
85    refresh: RefreshLut,
86}
87
88impl<SPI, BUSY, DC, RST, DELAY> Epd2in9<SPI, BUSY, DC, RST, DELAY>
89where
90    SPI: SpiDevice,
91    BUSY: InputPin,
92    DC: OutputPin,
93    RST: OutputPin,
94    DELAY: DelayNs,
95{
96    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
97        self.interface.reset(delay, 10_000, 10_000);
98
99        self.wait_until_idle(spi, delay)?;
100
101        // 3 Databytes:
102        // A[7:0]
103        // 0.. A[8]
104        // 0.. B[2:0]
105        // Default Values: A = Height of Screen (0x127), B = 0x00 (GD, SM and TB=0?)
106        self.interface
107            .cmd_with_data(spi, Command::DriverOutputControl, &[0x27, 0x01, 0x00])?;
108
109        // 3 Databytes: (and default values from datasheet and arduino)
110        // 1 .. A[6:0]  = 0xCF | 0xD7
111        // 1 .. B[6:0]  = 0xCE | 0xD6
112        // 1 .. C[6:0]  = 0x8D | 0x9D
113        //TODO: test
114        self.interface
115            .cmd_with_data(spi, Command::BoosterSoftStartControl, &[0xD7, 0xD6, 0x9D])?;
116
117        // One Databyte with value 0xA8 for 7V VCOM
118        self.interface
119            .cmd_with_data(spi, Command::WriteVcomRegister, &[0xA8])?;
120
121        // One Databyte with default value 0x1A for 4 dummy lines per gate
122        self.interface
123            .cmd_with_data(spi, Command::SetDummyLinePeriod, &[0x1A])?;
124
125        // One Databyte with default value 0x08 for 2us per line
126        self.interface
127            .cmd_with_data(spi, Command::SetGateLineWidth, &[0x08])?;
128
129        // One Databyte with default value 0x03
130        //  -> address: x increment, y increment, address counter is updated in x direction
131        self.interface
132            .cmd_with_data(spi, Command::DataEntryModeSetting, &[0x03])?;
133
134        self.set_lut(spi, delay, None)
135    }
136}
137
138impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
139    for Epd2in9<SPI, BUSY, DC, RST, DELAY>
140where
141    SPI: SpiDevice,
142    BUSY: InputPin,
143    DC: OutputPin,
144    RST: OutputPin,
145    DELAY: DelayNs,
146{
147    type DisplayColor = Color;
148    fn width(&self) -> u32 {
149        WIDTH
150    }
151
152    fn height(&self) -> u32 {
153        HEIGHT
154    }
155
156    fn new(
157        spi: &mut SPI,
158        busy: BUSY,
159        dc: DC,
160        rst: RST,
161        delay: &mut DELAY,
162        delay_us: Option<u32>,
163    ) -> Result<Self, SPI::Error> {
164        let interface = DisplayInterface::new(busy, dc, rst, delay_us);
165
166        let mut epd = Epd2in9 {
167            interface,
168            background_color: DEFAULT_BACKGROUND_COLOR,
169            refresh: RefreshLut::Full,
170        };
171
172        epd.init(spi, delay)?;
173
174        Ok(epd)
175    }
176
177    fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
178        self.wait_until_idle(spi, delay)?;
179        // 0x00 for Normal mode (Power on Reset), 0x01 for Deep Sleep Mode
180        //TODO: is 0x00 needed here? (see also epd1in54)
181        self.interface
182            .cmd_with_data(spi, Command::DeepSleepMode, &[0x00])?;
183        Ok(())
184    }
185
186    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
187        self.wait_until_idle(spi, delay)?;
188        self.init(spi, delay)?;
189        Ok(())
190    }
191
192    fn update_frame(
193        &mut self,
194        spi: &mut SPI,
195        buffer: &[u8],
196        delay: &mut DELAY,
197    ) -> Result<(), SPI::Error> {
198        self.wait_until_idle(spi, delay)?;
199        self.use_full_frame(spi, delay)?;
200
201        self.interface
202            .cmd_with_data(spi, Command::WriteRam, buffer)?;
203        Ok(())
204    }
205
206    //TODO: update description: last 3 bits will be ignored for width and x_pos
207    fn update_partial_frame(
208        &mut self,
209        spi: &mut SPI,
210        delay: &mut DELAY,
211        buffer: &[u8],
212        x: u32,
213        y: u32,
214        width: u32,
215        height: u32,
216    ) -> Result<(), SPI::Error> {
217        self.wait_until_idle(spi, delay)?;
218        self.set_ram_area(spi, x, y, x + width, y + height)?;
219        self.set_ram_counter(spi, delay, x, y)?;
220
221        self.interface
222            .cmd_with_data(spi, Command::WriteRam, buffer)?;
223        Ok(())
224    }
225
226    fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
227        self.wait_until_idle(spi, delay)?;
228        // enable clock signal, enable cp, display pattern -> 0xC4 (tested with the arduino version)
229        //TODO: test control_1 or control_2 with default value 0xFF (from the datasheet)
230        self.interface
231            .cmd_with_data(spi, Command::DisplayUpdateControl2, &[0xC4])?;
232
233        self.interface.cmd(spi, Command::MasterActivation)?;
234        // MASTER Activation should not be interupted to avoid currption of panel images
235        // therefore a terminate command is send
236        self.interface.cmd(spi, Command::Nop)?;
237        Ok(())
238    }
239
240    fn update_and_display_frame(
241        &mut self,
242        spi: &mut SPI,
243        buffer: &[u8],
244        delay: &mut DELAY,
245    ) -> Result<(), SPI::Error> {
246        self.update_frame(spi, buffer, delay)?;
247        self.display_frame(spi, delay)?;
248        Ok(())
249    }
250
251    fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
252        self.wait_until_idle(spi, delay)?;
253        self.use_full_frame(spi, delay)?;
254
255        // clear the ram with the background color
256        let color = self.background_color.get_byte_value();
257
258        self.interface.cmd(spi, Command::WriteRam)?;
259        self.interface
260            .data_x_times(spi, color, WIDTH / 8 * HEIGHT)?;
261        Ok(())
262    }
263
264    fn set_background_color(&mut self, background_color: Color) {
265        self.background_color = background_color;
266    }
267
268    fn background_color(&self) -> &Color {
269        &self.background_color
270    }
271
272    fn set_lut(
273        &mut self,
274        spi: &mut SPI,
275        delay: &mut DELAY,
276        refresh_rate: Option<RefreshLut>,
277    ) -> Result<(), SPI::Error> {
278        if let Some(refresh_lut) = refresh_rate {
279            self.refresh = refresh_lut;
280        }
281        match self.refresh {
282            RefreshLut::Full => self.set_lut_helper(spi, delay, &LUT_FULL_UPDATE),
283            RefreshLut::Quick => self.set_lut_helper(spi, delay, &LUT_PARTIAL_UPDATE),
284        }
285    }
286
287    fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
288        self.interface.wait_until_idle(delay, IS_BUSY_LOW);
289        Ok(())
290    }
291}
292
293impl<SPI, BUSY, DC, RST, DELAY> Epd2in9<SPI, BUSY, DC, RST, DELAY>
294where
295    SPI: SpiDevice,
296    BUSY: InputPin,
297    DC: OutputPin,
298    RST: OutputPin,
299    DELAY: DelayNs,
300{
301    fn use_full_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
302        // choose full frame/ram
303        self.set_ram_area(spi, 0, 0, WIDTH - 1, HEIGHT - 1)?;
304
305        // start from the beginning
306        self.set_ram_counter(spi, delay, 0, 0)
307    }
308
309    fn set_ram_area(
310        &mut self,
311        spi: &mut SPI,
312        start_x: u32,
313        start_y: u32,
314        end_x: u32,
315        end_y: u32,
316    ) -> Result<(), SPI::Error> {
317        assert!(start_x < end_x);
318        assert!(start_y < end_y);
319
320        // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
321        // aren't relevant
322        self.interface.cmd_with_data(
323            spi,
324            Command::SetRamXAddressStartEndPosition,
325            &[(start_x >> 3) as u8, (end_x >> 3) as u8],
326        )?;
327
328        // 2 Databytes: A[7:0] & 0..A[8] for each - start and end
329        self.interface.cmd_with_data(
330            spi,
331            Command::SetRamYAddressStartEndPosition,
332            &[
333                start_y as u8,
334                (start_y >> 8) as u8,
335                end_y as u8,
336                (end_y >> 8) as u8,
337            ],
338        )
339    }
340
341    fn set_ram_counter(
342        &mut self,
343        spi: &mut SPI,
344        delay: &mut DELAY,
345        x: u32,
346        y: u32,
347    ) -> Result<(), SPI::Error> {
348        self.wait_until_idle(spi, delay)?;
349        // x is positioned in bytes, so the last 3 bits which show the position inside a byte in the ram
350        // aren't relevant
351        self.interface
352            .cmd_with_data(spi, Command::SetRamXAddressCounter, &[(x >> 3) as u8])?;
353
354        // 2 Databytes: A[7:0] & 0..A[8]
355        self.interface.cmd_with_data(
356            spi,
357            Command::SetRamYAddressCounter,
358            &[y as u8, (y >> 8) as u8],
359        )?;
360        Ok(())
361    }
362
363    /// Set your own LUT, this function is also used internally for set_lut
364    fn set_lut_helper(
365        &mut self,
366        spi: &mut SPI,
367        delay: &mut DELAY,
368        buffer: &[u8],
369    ) -> Result<(), SPI::Error> {
370        self.wait_until_idle(spi, delay)?;
371        assert!(buffer.len() == 30);
372        self.interface
373            .cmd_with_data(spi, Command::WriteLutRegister, buffer)?;
374        Ok(())
375    }
376}
377
378#[cfg(test)]
379mod tests {
380    use super::*;
381
382    #[test]
383    fn epd_size() {
384        assert_eq!(WIDTH, 128);
385        assert_eq!(HEIGHT, 296);
386        assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White);
387    }
388}