epd_waveshare/epd4in2/
mod.rs

1//! A simple Driver for the Waveshare 4.2" E-Ink Display via SPI
2//!
3//!
4//! Build with the help of documentation/code from [Waveshare](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module),
5//! [Ben Krasnows partial Refresh tips](https://benkrasnow.blogspot.de/2017/10/fast-partial-refresh-on-42-e-paper.html) and
6//! the driver documents in the `pdfs`-folder as orientation.
7//!
8//! # Examples
9//!
10//!```rust, no_run
11//!# use embedded_hal_mock::eh1::*;
12//!# fn main() -> Result<(), embedded_hal::spi::ErrorKind> {
13//!use embedded_graphics::{
14//!    pixelcolor::BinaryColor::On as Black, prelude::*, primitives::{Line, PrimitiveStyle},
15//!};
16//!use epd_waveshare::{epd4in2::*, prelude::*};
17//!#
18//!# let expectations = [];
19//!# let mut spi = spi::Mock::new(&expectations);
20//!# let expectations = [];
21//!# let cs_pin = digital::Mock::new(&expectations);
22//!# let busy_in = digital::Mock::new(&expectations);
23//!# let dc = digital::Mock::new(&expectations);
24//!# let rst = digital::Mock::new(&expectations);
25//!# let mut delay = delay::NoopDelay::new();
26//!
27//!// Setup EPD
28//!let mut epd = Epd4in2::new(&mut spi, busy_in, dc, rst, &mut delay, None)?;
29//!
30//!// Use display graphics from embedded-graphics
31//!let mut display = Display4in2::default();
32//!
33//!// Use embedded graphics for drawing a line
34//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295))
35//!    .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1))
36//!    .draw(&mut display);
37//!
38//!    // Display updated frame
39//!epd.update_frame(&mut spi, &display.buffer(), &mut delay)?;
40//!epd.display_frame(&mut spi, &mut delay)?;
41//!
42//!// Set the EPD to sleep
43//!epd.sleep(&mut spi, &mut delay)?;
44//!# Ok(())
45//!# }
46//!```
47//!
48//!
49//!
50//! BE CAREFUL! The screen can get ghosting/burn-ins through the Partial Fast Update Drawing.
51
52use embedded_hal::{delay::*, digital::*, spi::SpiDevice};
53
54use crate::interface::DisplayInterface;
55use crate::traits::{InternalWiAdditions, QuickRefresh, RefreshLut, WaveshareDisplay};
56
57//The Lookup Tables for the Display
58mod constants;
59use crate::epd4in2::constants::*;
60
61/// Width of the display
62pub const WIDTH: u32 = 400;
63/// Height of the display
64pub const HEIGHT: u32 = 300;
65/// Default Background Color
66pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
67const IS_BUSY_LOW: bool = true;
68const SINGLE_BYTE_WRITE: bool = true;
69
70use crate::color::Color;
71
72pub(crate) mod command;
73use self::command::Command;
74use crate::buffer_len;
75
76/// Full size buffer for use with the 4in2 EPD
77#[cfg(feature = "graphics")]
78pub type Display4in2 = crate::graphics::Display<
79    WIDTH,
80    HEIGHT,
81    false,
82    { buffer_len(WIDTH as usize, HEIGHT as usize) },
83    Color,
84>;
85
86/// Epd4in2 driver
87///
88pub struct Epd4in2<SPI, BUSY, DC, RST, DELAY> {
89    /// Connection Interface
90    interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
91    /// Background Color
92    color: Color,
93    /// Refresh LUT
94    refresh: RefreshLut,
95}
96
97impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
98    for Epd4in2<SPI, BUSY, DC, RST, DELAY>
99where
100    SPI: SpiDevice,
101    BUSY: InputPin,
102    DC: OutputPin,
103    RST: OutputPin,
104    DELAY: DelayNs,
105{
106    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
107        // reset the device
108        self.interface.reset(delay, 10_000, 10_000);
109
110        // set the power settings
111        self.interface.cmd_with_data(
112            spi,
113            Command::PowerSetting,
114            &[0x03, 0x00, 0x2b, 0x2b, 0xff],
115        )?;
116
117        // start the booster
118        self.interface
119            .cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
120
121        // power on
122        self.command(spi, Command::PowerOn)?;
123        delay.delay_us(5000);
124        self.wait_until_idle(spi, delay)?;
125
126        // set the panel settings
127        self.cmd_with_data(spi, Command::PanelSetting, &[0x3F])?;
128
129        // Set Frequency, 200 Hz didn't work on my board
130        // 150Hz and 171Hz wasn't tested yet
131        // TODO: Test these other frequencies
132        // 3A 100HZ   29 150Hz 39 200HZ  31 171HZ DEFAULT: 3c 50Hz
133        self.cmd_with_data(spi, Command::PllControl, &[0x3A])?;
134
135        self.send_resolution(spi)?;
136
137        self.interface
138            .cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?;
139
140        //VBDF 17|D7 VBDW 97  VBDB 57  VBDF F7  VBDW 77  VBDB 37  VBDR B7
141        self.interface
142            .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x97])?;
143
144        self.set_lut(spi, delay, None)?;
145
146        self.wait_until_idle(spi, delay)?;
147        Ok(())
148    }
149}
150
151impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
152    for Epd4in2<SPI, BUSY, DC, RST, DELAY>
153where
154    SPI: SpiDevice,
155    BUSY: InputPin,
156    DC: OutputPin,
157    RST: OutputPin,
158    DELAY: DelayNs,
159{
160    type DisplayColor = Color;
161    fn new(
162        spi: &mut SPI,
163        busy: BUSY,
164        dc: DC,
165        rst: RST,
166        delay: &mut DELAY,
167        delay_us: Option<u32>,
168    ) -> Result<Self, SPI::Error> {
169        let interface = DisplayInterface::new(busy, dc, rst, delay_us);
170        let color = DEFAULT_BACKGROUND_COLOR;
171
172        let mut epd = Epd4in2 {
173            interface,
174            color,
175            refresh: RefreshLut::Full,
176        };
177
178        epd.init(spi, delay)?;
179
180        Ok(epd)
181    }
182
183    fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
184        self.wait_until_idle(spi, delay)?;
185        self.interface
186            .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x17])?; //border floating
187        self.command(spi, Command::VcmDcSetting)?; // VCOM to 0V
188        self.command(spi, Command::PanelSetting)?;
189
190        self.command(spi, Command::PowerSetting)?; //VG&VS to 0V fast
191        for _ in 0..4 {
192            self.send_data(spi, &[0x00])?;
193        }
194
195        self.command(spi, Command::PowerOff)?;
196        self.wait_until_idle(spi, delay)?;
197        self.interface
198            .cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
199        Ok(())
200    }
201
202    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
203        self.init(spi, delay)
204    }
205
206    fn set_background_color(&mut self, color: Color) {
207        self.color = color;
208    }
209
210    fn background_color(&self) -> &Color {
211        &self.color
212    }
213
214    fn width(&self) -> u32 {
215        WIDTH
216    }
217
218    fn height(&self) -> u32 {
219        HEIGHT
220    }
221
222    fn update_frame(
223        &mut self,
224        spi: &mut SPI,
225        buffer: &[u8],
226        delay: &mut DELAY,
227    ) -> Result<(), SPI::Error> {
228        self.wait_until_idle(spi, delay)?;
229        let color_value = self.color.get_byte_value();
230
231        self.interface.cmd(spi, Command::DataStartTransmission1)?;
232        self.interface
233            .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
234
235        self.interface
236            .cmd_with_data(spi, Command::DataStartTransmission2, buffer)?;
237        Ok(())
238    }
239
240    fn update_partial_frame(
241        &mut self,
242        spi: &mut SPI,
243        delay: &mut DELAY,
244        buffer: &[u8],
245        x: u32,
246        y: u32,
247        width: u32,
248        height: u32,
249    ) -> Result<(), SPI::Error> {
250        self.wait_until_idle(spi, delay)?;
251        if buffer.len() as u32 != width / 8 * height {
252            //TODO: panic!! or sth like that
253            //return Err("Wrong buffersize");
254        }
255
256        self.command(spi, Command::PartialIn)?;
257        self.command(spi, Command::PartialWindow)?;
258        self.send_data(spi, &[(x >> 8) as u8])?;
259        let tmp = x & 0xf8;
260        self.send_data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored
261        let tmp = tmp + width - 1;
262        self.send_data(spi, &[(tmp >> 8) as u8])?;
263        self.send_data(spi, &[(tmp | 0x07) as u8])?;
264
265        self.send_data(spi, &[(y >> 8) as u8])?;
266        self.send_data(spi, &[y as u8])?;
267
268        self.send_data(spi, &[((y + height - 1) >> 8) as u8])?;
269        self.send_data(spi, &[(y + height - 1) as u8])?;
270
271        self.send_data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default)
272
273        //TODO: handle dtm somehow
274        let is_dtm1 = false;
275        if is_dtm1 {
276            self.command(spi, Command::DataStartTransmission1)? //TODO: check if data_start transmission 1 also needs "old"/background data here
277        } else {
278            self.command(spi, Command::DataStartTransmission2)?
279        }
280
281        self.send_data(spi, buffer)?;
282
283        self.command(spi, Command::PartialOut)?;
284        Ok(())
285    }
286
287    fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
288        self.wait_until_idle(spi, delay)?;
289        self.command(spi, Command::DisplayRefresh)?;
290        Ok(())
291    }
292
293    fn update_and_display_frame(
294        &mut self,
295        spi: &mut SPI,
296        buffer: &[u8],
297        delay: &mut DELAY,
298    ) -> Result<(), SPI::Error> {
299        self.update_frame(spi, buffer, delay)?;
300        self.command(spi, Command::DisplayRefresh)?;
301        Ok(())
302    }
303
304    fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
305        self.wait_until_idle(spi, delay)?;
306        self.send_resolution(spi)?;
307
308        let color_value = self.color.get_byte_value();
309
310        self.interface.cmd(spi, Command::DataStartTransmission1)?;
311        self.interface
312            .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
313
314        self.interface.cmd(spi, Command::DataStartTransmission2)?;
315        self.interface
316            .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
317        Ok(())
318    }
319
320    fn set_lut(
321        &mut self,
322        spi: &mut SPI,
323        delay: &mut DELAY,
324        refresh_rate: Option<RefreshLut>,
325    ) -> Result<(), SPI::Error> {
326        if let Some(refresh_lut) = refresh_rate {
327            self.refresh = refresh_lut;
328        }
329        match self.refresh {
330            RefreshLut::Full => {
331                self.set_lut_helper(spi, delay, &LUT_VCOM0, &LUT_WW, &LUT_BW, &LUT_WB, &LUT_BB)
332            }
333            RefreshLut::Quick => self.set_lut_helper(
334                spi,
335                delay,
336                &LUT_VCOM0_QUICK,
337                &LUT_WW_QUICK,
338                &LUT_BW_QUICK,
339                &LUT_WB_QUICK,
340                &LUT_BB_QUICK,
341            ),
342        }
343    }
344
345    fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
346        self.interface.wait_until_idle(delay, IS_BUSY_LOW);
347        Ok(())
348    }
349}
350
351impl<SPI, BUSY, DC, RST, DELAY> Epd4in2<SPI, BUSY, DC, RST, DELAY>
352where
353    SPI: SpiDevice,
354    BUSY: InputPin,
355    DC: OutputPin,
356    RST: OutputPin,
357    DELAY: DelayNs,
358{
359    fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> {
360        self.interface.cmd(spi, command)
361    }
362
363    fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
364        self.interface.data(spi, data)
365    }
366
367    fn cmd_with_data(
368        &mut self,
369        spi: &mut SPI,
370        command: Command,
371        data: &[u8],
372    ) -> Result<(), SPI::Error> {
373        self.interface.cmd_with_data(spi, command, data)
374    }
375
376    fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
377        let w = self.width();
378        let h = self.height();
379
380        self.command(spi, Command::ResolutionSetting)?;
381        self.send_data(spi, &[(w >> 8) as u8])?;
382        self.send_data(spi, &[w as u8])?;
383        self.send_data(spi, &[(h >> 8) as u8])?;
384        self.send_data(spi, &[h as u8])
385    }
386
387    #[allow(clippy::too_many_arguments)]
388    fn set_lut_helper(
389        &mut self,
390        spi: &mut SPI,
391        delay: &mut DELAY,
392        lut_vcom: &[u8],
393        lut_ww: &[u8],
394        lut_bw: &[u8],
395        lut_wb: &[u8],
396        lut_bb: &[u8],
397    ) -> Result<(), SPI::Error> {
398        self.wait_until_idle(spi, delay)?;
399        // LUT VCOM
400        self.cmd_with_data(spi, Command::LutForVcom, lut_vcom)?;
401
402        // LUT WHITE to WHITE
403        self.cmd_with_data(spi, Command::LutWhiteToWhite, lut_ww)?;
404
405        // LUT BLACK to WHITE
406        self.cmd_with_data(spi, Command::LutBlackToWhite, lut_bw)?;
407
408        // LUT WHITE to BLACK
409        self.cmd_with_data(spi, Command::LutWhiteToBlack, lut_wb)?;
410
411        // LUT BLACK to BLACK
412        self.cmd_with_data(spi, Command::LutBlackToBlack, lut_bb)?;
413        Ok(())
414    }
415
416    /// Helper function. Sets up the display to send pixel data to a custom
417    /// starting point.
418    pub fn shift_display(
419        &mut self,
420        spi: &mut SPI,
421        x: u32,
422        y: u32,
423        width: u32,
424        height: u32,
425    ) -> Result<(), SPI::Error> {
426        self.send_data(spi, &[(x >> 8) as u8])?;
427        let tmp = x & 0xf8;
428        self.send_data(spi, &[tmp as u8])?; // x should be the multiple of 8, the last 3 bit will always be ignored
429        let tmp = tmp + width - 1;
430        self.send_data(spi, &[(tmp >> 8) as u8])?;
431        self.send_data(spi, &[(tmp | 0x07) as u8])?;
432
433        self.send_data(spi, &[(y >> 8) as u8])?;
434        self.send_data(spi, &[y as u8])?;
435
436        self.send_data(spi, &[((y + height - 1) >> 8) as u8])?;
437        self.send_data(spi, &[(y + height - 1) as u8])?;
438
439        self.send_data(spi, &[0x01])?; // Gates scan both inside and outside of the partial window. (default)
440
441        Ok(())
442    }
443}
444
445impl<SPI, BUSY, DC, RST, DELAY> QuickRefresh<SPI, BUSY, DC, RST, DELAY>
446    for Epd4in2<SPI, BUSY, DC, RST, DELAY>
447where
448    SPI: SpiDevice,
449    BUSY: InputPin,
450    DC: OutputPin,
451    RST: OutputPin,
452    DELAY: DelayNs,
453{
454    /// To be followed immediately after by `update_old_frame`.
455    fn update_old_frame(
456        &mut self,
457        spi: &mut SPI,
458        buffer: &[u8],
459        delay: &mut DELAY,
460    ) -> Result<(), SPI::Error> {
461        self.wait_until_idle(spi, delay)?;
462
463        self.interface.cmd(spi, Command::DataStartTransmission1)?;
464
465        self.interface.data(spi, buffer)?;
466
467        Ok(())
468    }
469
470    /// To be used immediately after `update_old_frame`.
471    fn update_new_frame(
472        &mut self,
473        spi: &mut SPI,
474        buffer: &[u8],
475        delay: &mut DELAY,
476    ) -> Result<(), SPI::Error> {
477        self.wait_until_idle(spi, delay)?;
478        // self.send_resolution(spi)?;
479
480        self.interface.cmd(spi, Command::DataStartTransmission2)?;
481
482        self.interface.data(spi, buffer)?;
483
484        Ok(())
485    }
486
487    /// This is a wrapper around `display_frame` for using this device as a true
488    /// `QuickRefresh` device.
489    fn display_new_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
490        self.display_frame(spi, delay)
491    }
492
493    /// This is wrapper around `update_new_frame` and `display_frame` for using
494    /// this device as a true `QuickRefresh` device.
495    ///
496    /// To be used immediately after `update_old_frame`.
497    fn update_and_display_new_frame(
498        &mut self,
499        spi: &mut SPI,
500        buffer: &[u8],
501        delay: &mut DELAY,
502    ) -> Result<(), SPI::Error> {
503        self.update_new_frame(spi, buffer, delay)?;
504        self.display_frame(spi, delay)
505    }
506
507    fn update_partial_old_frame(
508        &mut self,
509        spi: &mut SPI,
510        delay: &mut DELAY,
511        buffer: &[u8],
512        x: u32,
513        y: u32,
514        width: u32,
515        height: u32,
516    ) -> Result<(), SPI::Error> {
517        self.wait_until_idle(spi, delay)?;
518
519        if buffer.len() as u32 != width / 8 * height {
520            //TODO: panic!! or sth like that
521            //return Err("Wrong buffersize");
522        }
523
524        self.interface.cmd(spi, Command::PartialIn)?;
525        self.interface.cmd(spi, Command::PartialWindow)?;
526
527        self.shift_display(spi, x, y, width, height)?;
528
529        self.interface.cmd(spi, Command::DataStartTransmission1)?;
530
531        self.interface.data(spi, buffer)?;
532
533        Ok(())
534    }
535
536    /// Always call `update_partial_old_frame` before this, with buffer-updating code
537    /// between the calls.
538    fn update_partial_new_frame(
539        &mut self,
540        spi: &mut SPI,
541        delay: &mut DELAY,
542        buffer: &[u8],
543        x: u32,
544        y: u32,
545        width: u32,
546        height: u32,
547    ) -> Result<(), SPI::Error> {
548        self.wait_until_idle(spi, delay)?;
549        if buffer.len() as u32 != width / 8 * height {
550            //TODO: panic!! or sth like that
551            //return Err("Wrong buffersize");
552        }
553
554        self.shift_display(spi, x, y, width, height)?;
555
556        self.interface.cmd(spi, Command::DataStartTransmission2)?;
557
558        self.interface.data(spi, buffer)?;
559
560        self.interface.cmd(spi, Command::PartialOut)?;
561        Ok(())
562    }
563
564    fn clear_partial_frame(
565        &mut self,
566        spi: &mut SPI,
567        delay: &mut DELAY,
568        x: u32,
569        y: u32,
570        width: u32,
571        height: u32,
572    ) -> Result<(), SPI::Error> {
573        self.wait_until_idle(spi, delay)?;
574        self.send_resolution(spi)?;
575
576        let color_value = self.color.get_byte_value();
577
578        self.interface.cmd(spi, Command::PartialIn)?;
579        self.interface.cmd(spi, Command::PartialWindow)?;
580
581        self.shift_display(spi, x, y, width, height)?;
582
583        self.interface.cmd(spi, Command::DataStartTransmission1)?;
584        self.interface
585            .data_x_times(spi, color_value, width / 8 * height)?;
586
587        self.interface.cmd(spi, Command::DataStartTransmission2)?;
588        self.interface
589            .data_x_times(spi, color_value, width / 8 * height)?;
590
591        self.interface.cmd(spi, Command::PartialOut)?;
592        Ok(())
593    }
594}
595
596#[cfg(test)]
597mod tests {
598    use super::*;
599
600    #[test]
601    fn epd_size() {
602        assert_eq!(WIDTH, 400);
603        assert_eq!(HEIGHT, 300);
604        assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White);
605    }
606}