epd_waveshare/epd7in5b_v2/
mod.rs

1//! A simple Driver for the Waveshare 7.5" (B) E-Ink Display (V2 and V3) via SPI
2//!
3//! # References
4//!
5//! - [Datasheet](https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT)
6//! - [Waveshare C driver](https://github.com/waveshare/e-Paper/blob/702def0/RaspberryPi%26JetsonNano/c/lib/e-Paper/EPD_7in5_V2.c)
7//! - [Waveshare Python driver](https://github.com/waveshare/e-Paper/blob/702def0/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd7in5_V2.py)
8//!
9//! Important note for V2:
10//! Revision V2 has been released on 2019.11, the resolution is upgraded to 800×480, from 640×384 of V1.
11//! The hardware and interface of V2 are compatible with V1, however, the related software should be updated.
12
13use embedded_hal::{
14    delay::DelayNs,
15    digital::{InputPin, OutputPin},
16    spi::SpiDevice,
17};
18
19use crate::color::TriColor;
20use crate::interface::DisplayInterface;
21use crate::traits::{
22    InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
23};
24
25pub(crate) mod command;
26use self::command::Command;
27use crate::buffer_len;
28
29/// Full size buffer for use with the 7in5b v2 EPD
30#[cfg(feature = "graphics")]
31pub type Display7in5 = crate::graphics::Display<
32    WIDTH,
33    HEIGHT,
34    false,
35    { buffer_len(WIDTH as usize, HEIGHT as usize * 2) },
36    TriColor,
37>;
38
39/// Width of the display
40pub const WIDTH: u32 = 800;
41/// Height of the display
42pub const HEIGHT: u32 = 480;
43/// Default Background Color
44pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White;
45
46/// Number of bytes for b/w buffer and same for chromatic buffer bits
47const NUM_DISPLAY_BITS: usize = WIDTH as usize / 8 * HEIGHT as usize;
48const IS_BUSY_LOW: bool = true;
49const SINGLE_BYTE_WRITE: bool = false;
50
51/// Epd7in5 (V2) driver
52///
53pub struct Epd7in5<SPI, BUSY, DC, RST, DELAY> {
54    /// Connection Interface
55    interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
56    /// Background Color
57    color: TriColor,
58}
59
60impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
61    for Epd7in5<SPI, BUSY, DC, RST, DELAY>
62where
63    SPI: SpiDevice,
64    BUSY: InputPin,
65    DC: OutputPin,
66    RST: OutputPin,
67    DELAY: DelayNs,
68{
69    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
70        // Reset the device
71        // C driver does 200/2 original rust driver does 10/2
72        self.interface.reset(delay, 200_000, 2_000);
73
74        // V2 procedure as described here:
75        // https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd7in5bc_V2.py
76        // and as per specs:
77        // https://www.waveshare.com/w/upload/6/60/7.5inch_e-Paper_V2_Specification.pdf
78
79        self.cmd_with_data(spi, Command::PowerSetting, &[0x07, 0x07, 0x3F, 0x3F])?;
80        self.command(spi, Command::PowerOn)?;
81        // C driver adds a static 100ms delay here
82        self.wait_until_idle(spi, delay)?;
83        // Done, but this is also the default
84        // 0x1F = B/W mode ? doesnt seem to work
85        self.cmd_with_data(spi, Command::PanelSetting, &[0x0F])?;
86        // Not done in C driver, this is the default
87        //self.cmd_with_data(spi, Command::PllControl, &[0x06])?;
88        self.cmd_with_data(spi, Command::TconResolution, &[0x03, 0x20, 0x01, 0xE0])?;
89        // Documentation removed in v3 but done in v2 and works in v3
90        self.cmd_with_data(spi, Command::DualSpi, &[0x00])?;
91        //                    0x10 in BW mode  (Work ?) V
92        //                    0x12 in BW mode to disable new/old thing
93        //                    0x01 -> Black border
94        //                    0x11 -> White norder
95        //                    0x21 -> Red border
96        //                    0x31 -> don't touch border
97        //                    the second nibble can change polarity (may be easier for default
98        //                    display initialization)                   V
99        self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x11, 0x07])?;
100        // This is the default
101        self.cmd_with_data(spi, Command::TconSetting, &[0x22])?;
102        self.cmd_with_data(spi, Command::SpiFlashControl, &[0x00, 0x00, 0x00, 0x00])?;
103        // Not in C driver
104        self.wait_until_idle(spi, delay)?;
105        Ok(())
106    }
107}
108
109impl<SPI, BUSY, DC, RST, DELAY> WaveshareThreeColorDisplay<SPI, BUSY, DC, RST, DELAY>
110    for Epd7in5<SPI, BUSY, DC, RST, DELAY>
111where
112    SPI: SpiDevice,
113    BUSY: InputPin,
114    DC: OutputPin,
115    RST: OutputPin,
116    DELAY: DelayNs,
117{
118    fn update_color_frame(
119        &mut self,
120        spi: &mut SPI,
121        delay: &mut DELAY,
122        black: &[u8],
123        chromatic: &[u8],
124    ) -> Result<(), SPI::Error> {
125        self.update_achromatic_frame(spi, delay, black)?;
126        self.update_chromatic_frame(spi, delay, chromatic)
127    }
128
129    /// Update only the black/white data of the display.
130    ///
131    /// Finish by calling `update_chromatic_frame`.
132    fn update_achromatic_frame(
133        &mut self,
134        spi: &mut SPI,
135        _delay: &mut DELAY,
136        black: &[u8],
137    ) -> Result<(), SPI::Error> {
138        self.interface.cmd(spi, Command::DataStartTransmission1)?;
139        self.interface.data(spi, black)?;
140        self.interface.cmd(spi, Command::DataStop)?;
141        Ok(())
142    }
143
144    /// Update only chromatic data of the display.
145    ///
146    /// This data takes precedence over the black/white data.
147    fn update_chromatic_frame(
148        &mut self,
149        spi: &mut SPI,
150        delay: &mut DELAY,
151        chromatic: &[u8],
152    ) -> Result<(), SPI::Error> {
153        self.interface.cmd(spi, Command::DataStartTransmission2)?;
154        self.interface.data(spi, chromatic)?;
155        self.interface.cmd(spi, Command::DataStop)?;
156
157        self.wait_until_idle(spi, delay)?;
158        Ok(())
159    }
160}
161
162impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
163    for Epd7in5<SPI, BUSY, DC, RST, DELAY>
164where
165    SPI: SpiDevice,
166    BUSY: InputPin,
167    DC: OutputPin,
168    RST: OutputPin,
169    DELAY: DelayNs,
170{
171    type DisplayColor = TriColor;
172    fn new(
173        spi: &mut SPI,
174        busy: BUSY,
175        dc: DC,
176        rst: RST,
177        delay: &mut DELAY,
178        delay_us: Option<u32>,
179    ) -> Result<Self, SPI::Error> {
180        let interface = DisplayInterface::new(busy, dc, rst, delay_us);
181        let color = DEFAULT_BACKGROUND_COLOR;
182
183        let mut epd = Epd7in5 { interface, color };
184
185        epd.init(spi, delay)?;
186
187        Ok(epd)
188    }
189
190    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
191        self.init(spi, delay)
192    }
193
194    fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
195        self.wait_until_idle(spi, delay)?;
196        self.command(spi, Command::PowerOff)?;
197        self.wait_until_idle(spi, delay)?;
198        self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
199        Ok(())
200    }
201
202    fn update_frame(
203        &mut self,
204        spi: &mut SPI,
205        buffer: &[u8],
206        delay: &mut DELAY,
207    ) -> Result<(), SPI::Error> {
208        self.wait_until_idle(spi, delay)?;
209        // (B) version sends one buffer for black and one for red
210        self.cmd_with_data(
211            spi,
212            Command::DataStartTransmission1,
213            &buffer[..NUM_DISPLAY_BITS],
214        )?;
215        self.cmd_with_data(
216            spi,
217            Command::DataStartTransmission2,
218            &buffer[NUM_DISPLAY_BITS..],
219        )?;
220        self.interface.cmd(spi, Command::DataStop)?;
221        Ok(())
222    }
223
224    fn update_partial_frame(
225        &mut self,
226        _spi: &mut SPI,
227        _delay: &mut DELAY,
228        _buffer: &[u8],
229        _x: u32,
230        _y: u32,
231        _width: u32,
232        _height: u32,
233    ) -> Result<(), SPI::Error> {
234        unimplemented!()
235    }
236
237    fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
238        self.wait_until_idle(spi, delay)?;
239        self.command(spi, Command::DisplayRefresh)?;
240        Ok(())
241    }
242
243    fn update_and_display_frame(
244        &mut self,
245        spi: &mut SPI,
246        buffer: &[u8],
247        delay: &mut DELAY,
248    ) -> Result<(), SPI::Error> {
249        self.update_frame(spi, buffer, delay)?;
250        self.command(spi, Command::DisplayRefresh)?;
251        Ok(())
252    }
253
254    fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
255        self.wait_until_idle(spi, delay)?;
256        self.send_resolution(spi)?;
257
258        self.command(spi, Command::DataStartTransmission1)?;
259        self.interface.data_x_times(spi, 0xFF, WIDTH / 8 * HEIGHT)?;
260
261        self.command(spi, Command::DataStartTransmission2)?;
262        self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?;
263
264        self.interface.cmd(spi, Command::DataStop)?;
265
266        self.command(spi, Command::DisplayRefresh)?;
267
268        Ok(())
269    }
270
271    fn set_background_color(&mut self, color: Self::DisplayColor) {
272        self.color = color;
273    }
274
275    fn background_color(&self) -> &Self::DisplayColor {
276        &self.color
277    }
278
279    fn width(&self) -> u32 {
280        WIDTH
281    }
282
283    fn height(&self) -> u32 {
284        HEIGHT
285    }
286
287    fn set_lut(
288        &mut self,
289        _spi: &mut SPI,
290        _delay: &mut DELAY,
291        _refresh_rate: Option<RefreshLut>,
292    ) -> Result<(), SPI::Error> {
293        unimplemented!();
294    }
295
296    /// wait
297    fn wait_until_idle(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
298        self.interface
299            .wait_until_idle_with_cmd(spi, delay, IS_BUSY_LOW, Command::GetStatus)
300    }
301}
302
303impl<SPI, BUSY, DC, RST, DELAY> Epd7in5<SPI, BUSY, DC, RST, DELAY>
304where
305    SPI: SpiDevice,
306    BUSY: InputPin,
307    DC: OutputPin,
308    RST: OutputPin,
309    DELAY: DelayNs,
310{
311    /// temporary replacement for missing delay in the trait to call wait_until_idle
312    #[allow(clippy::too_many_arguments)]
313    pub fn update_partial_frame2(
314        &mut self,
315        spi: &mut SPI,
316        buffer: &[u8],
317        x: u32,
318        y: u32,
319        width: u32,
320        height: u32,
321        delay: &mut DELAY,
322    ) -> Result<(), SPI::Error> {
323        self.wait_until_idle(spi, delay)?;
324        if buffer.len() as u32 != width / 8 * height {
325            //TODO panic or error
326        }
327
328        let hrst_upper = (x / 8) as u8 >> 5;
329        let hrst_lower = ((x / 8) << 3) as u8;
330        let hred_upper = ((x + width) / 8 - 1) as u8 >> 5;
331        let hred_lower = (((x + width) / 8 - 1) << 3) as u8 | 0b111;
332        let vrst_upper = (y >> 8) as u8;
333        let vrst_lower = y as u8;
334        let vred_upper = ((y + height - 1) >> 8) as u8;
335        let vred_lower = (y + height - 1) as u8;
336        let pt_scan = 0x01; // Gates scan both inside and outside of the partial window. (default)
337
338        self.command(spi, Command::PartialIn)?;
339        self.cmd_with_data(
340            spi,
341            Command::PartialWindow,
342            &[
343                hrst_upper, hrst_lower, hred_upper, hred_lower, vrst_upper, vrst_lower, vred_upper,
344                vred_lower, pt_scan,
345            ],
346        )?;
347        let half = buffer.len() / 2;
348        self.cmd_with_data(spi, Command::DataStartTransmission1, &buffer[..half])?;
349        self.cmd_with_data(spi, Command::DataStartTransmission2, &buffer[half..])?;
350
351        self.command(spi, Command::DisplayRefresh)?;
352        self.wait_until_idle(spi, delay)?;
353
354        self.command(spi, Command::PartialOut)?;
355        Ok(())
356    }
357
358    fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> {
359        self.interface.cmd(spi, command)
360    }
361
362    fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
363        self.interface.data(spi, data)
364    }
365
366    fn cmd_with_data(
367        &mut self,
368        spi: &mut SPI,
369        command: Command,
370        data: &[u8],
371    ) -> Result<(), SPI::Error> {
372        self.interface.cmd_with_data(spi, command, data)
373    }
374
375    fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
376        let w = self.width();
377        let h = self.height();
378
379        self.command(spi, Command::TconResolution)?;
380        self.send_data(spi, &[(w >> 8) as u8])?;
381        self.send_data(spi, &[w as u8])?;
382        self.send_data(spi, &[(h >> 8) as u8])?;
383        self.send_data(spi, &[h as u8])
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390
391    #[test]
392    fn epd_size() {
393        assert_eq!(WIDTH, 800);
394        assert_eq!(HEIGHT, 480);
395        assert_eq!(DEFAULT_BACKGROUND_COLOR, TriColor::White);
396    }
397}