epd_waveshare/epd2in7b/
mod.rs

1//! A simple Driver for the Waveshare 2.7" B Tri-Color E-Ink Display via SPI
2//!
3//! [Documentation](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_(B))
4
5use embedded_hal::{delay::*, digital::*, spi::SpiDevice};
6
7use crate::interface::DisplayInterface;
8use crate::traits::{
9    InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
10};
11
12// The Lookup Tables for the Display
13mod constants;
14use crate::epd2in7b::constants::*;
15
16/// Width of the display
17pub const WIDTH: u32 = 176;
18/// Height of the display
19pub const HEIGHT: u32 = 264;
20/// Default Background Color
21pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White;
22const IS_BUSY_LOW: bool = true;
23const SINGLE_BYTE_WRITE: bool = true;
24
25use crate::color::Color;
26
27pub(crate) mod command;
28use self::command::Command;
29use crate::buffer_len;
30
31/// Full size buffer for use with the 2in7B EPD
32/// TODO this should be a TriColor, but let's keep it as is at first
33#[cfg(feature = "graphics")]
34pub type Display2in7b = crate::graphics::Display<
35    WIDTH,
36    HEIGHT,
37    false,
38    { buffer_len(WIDTH as usize, HEIGHT as usize) },
39    Color,
40>;
41
42/// Epd2in7b driver
43pub struct Epd2in7b<SPI, BUSY, DC, RST, DELAY> {
44    /// Connection Interface
45    interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
46    /// Background Color
47    color: Color,
48}
49
50impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
51    for Epd2in7b<SPI, BUSY, DC, RST, DELAY>
52where
53    SPI: SpiDevice,
54    BUSY: InputPin,
55    DC: OutputPin,
56    RST: OutputPin,
57    DELAY: DelayNs,
58{
59    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
60        // reset the device
61        self.interface.reset(delay, 10_000, 2_000);
62
63        // power on
64        self.command(spi, Command::PowerOn)?;
65        delay.delay_us(5000);
66        self.wait_until_idle(spi, delay)?;
67
68        // set panel settings, 0xbf is bw, 0xaf is multi-color
69        self.interface
70            .cmd_with_data(spi, Command::PanelSetting, &[0xaf])?;
71
72        // pll control
73        self.interface
74            .cmd_with_data(spi, Command::PllControl, &[0x3a])?;
75
76        // set the power settings
77        self.interface.cmd_with_data(
78            spi,
79            Command::PowerSetting,
80            &[0x03, 0x00, 0x2b, 0x2b, 0x09],
81        )?;
82
83        // start the booster
84        self.interface
85            .cmd_with_data(spi, Command::BoosterSoftStart, &[0x07, 0x07, 0x17])?;
86
87        // power optimization
88        self.interface
89            .cmd_with_data(spi, Command::PowerOptimization, &[0x60, 0xa5])?;
90        self.interface
91            .cmd_with_data(spi, Command::PowerOptimization, &[0x89, 0xa5])?;
92        self.interface
93            .cmd_with_data(spi, Command::PowerOptimization, &[0x90, 0x00])?;
94        self.interface
95            .cmd_with_data(spi, Command::PowerOptimization, &[0x93, 0x2a])?;
96        self.interface
97            .cmd_with_data(spi, Command::PowerOptimization, &[0x73, 0x41])?;
98
99        self.interface
100            .cmd_with_data(spi, Command::VcmDcSetting, &[0x12])?;
101
102        self.interface
103            .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x87])?;
104
105        self.set_lut(spi, delay, None)?;
106
107        self.interface
108            .cmd_with_data(spi, Command::PartialDisplayRefresh, &[0x00])?;
109
110        self.wait_until_idle(spi, delay)?;
111        Ok(())
112    }
113}
114
115impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
116    for Epd2in7b<SPI, BUSY, DC, RST, DELAY>
117where
118    SPI: SpiDevice,
119    BUSY: InputPin,
120    DC: OutputPin,
121    RST: OutputPin,
122    DELAY: DelayNs,
123{
124    type DisplayColor = Color;
125    fn new(
126        spi: &mut SPI,
127        busy: BUSY,
128        dc: DC,
129        rst: RST,
130        delay: &mut DELAY,
131        delay_us: Option<u32>,
132    ) -> Result<Self, SPI::Error> {
133        let interface = DisplayInterface::new(busy, dc, rst, delay_us);
134        let color = DEFAULT_BACKGROUND_COLOR;
135
136        let mut epd = Epd2in7b { interface, color };
137
138        epd.init(spi, delay)?;
139
140        Ok(epd)
141    }
142
143    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
144        self.init(spi, delay)
145    }
146
147    fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
148        self.wait_until_idle(spi, delay)?;
149        self.interface
150            .cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0xf7])?;
151
152        self.command(spi, Command::PowerOff)?;
153        self.wait_until_idle(spi, delay)?;
154        self.interface
155            .cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
156        Ok(())
157    }
158
159    fn update_frame(
160        &mut self,
161        spi: &mut SPI,
162        buffer: &[u8],
163        _delay: &mut DELAY,
164    ) -> Result<(), SPI::Error> {
165        self.interface.cmd(spi, Command::DataStartTransmission1)?;
166        self.send_buffer_helper(spi, buffer)?;
167
168        // Clear chromatic layer since we won't be using it here
169        self.interface.cmd(spi, Command::DataStartTransmission2)?;
170        self.interface
171            .data_x_times(spi, !self.color.get_byte_value(), WIDTH / 8 * HEIGHT)?;
172
173        self.interface.cmd(spi, Command::DataStop)?;
174        Ok(())
175    }
176
177    fn update_partial_frame(
178        &mut self,
179        spi: &mut SPI,
180        delay: &mut DELAY,
181        buffer: &[u8],
182        x: u32,
183        y: u32,
184        width: u32,
185        height: u32,
186    ) -> Result<(), SPI::Error> {
187        self.interface
188            .cmd(spi, Command::PartialDataStartTransmission1)?;
189
190        self.send_data(spi, &[(x >> 8) as u8])?;
191        self.send_data(spi, &[(x & 0xf8) as u8])?;
192        self.send_data(spi, &[(y >> 8) as u8])?;
193        self.send_data(spi, &[(y & 0xff) as u8])?;
194        self.send_data(spi, &[(width >> 8) as u8])?;
195        self.send_data(spi, &[(width & 0xf8) as u8])?;
196        self.send_data(spi, &[(height >> 8) as u8])?;
197        self.send_data(spi, &[(height & 0xff) as u8])?;
198        self.wait_until_idle(spi, delay)?;
199
200        self.send_buffer_helper(spi, buffer)?;
201
202        self.interface.cmd(spi, Command::DataStop)
203    }
204
205    fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
206        self.command(spi, Command::DisplayRefresh)?;
207        self.wait_until_idle(spi, delay)?;
208        Ok(())
209    }
210
211    fn update_and_display_frame(
212        &mut self,
213        spi: &mut SPI,
214        buffer: &[u8],
215        delay: &mut DELAY,
216    ) -> Result<(), SPI::Error> {
217        self.update_frame(spi, buffer, delay)?;
218        self.command(spi, Command::DisplayRefresh)?;
219        Ok(())
220    }
221
222    fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
223        self.wait_until_idle(spi, delay)?;
224
225        let color_value = self.color.get_byte_value();
226        self.interface.cmd(spi, Command::DataStartTransmission1)?;
227        self.interface
228            .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
229
230        self.interface.cmd(spi, Command::DataStop)?;
231
232        self.interface.cmd(spi, Command::DataStartTransmission2)?;
233        self.interface
234            .data_x_times(spi, color_value, WIDTH / 8 * HEIGHT)?;
235        self.interface.cmd(spi, Command::DataStop)?;
236        Ok(())
237    }
238
239    fn set_background_color(&mut self, color: Color) {
240        self.color = color;
241    }
242
243    fn background_color(&self) -> &Color {
244        &self.color
245    }
246
247    fn width(&self) -> u32 {
248        WIDTH
249    }
250
251    fn height(&self) -> u32 {
252        HEIGHT
253    }
254
255    fn set_lut(
256        &mut self,
257        spi: &mut SPI,
258        delay: &mut DELAY,
259        _refresh_rate: Option<RefreshLut>,
260    ) -> Result<(), SPI::Error> {
261        self.wait_until_idle(spi, delay)?;
262        self.cmd_with_data(spi, Command::LutForVcom, &LUT_VCOM_DC)?;
263        self.cmd_with_data(spi, Command::LutWhiteToWhite, &LUT_WW)?;
264        self.cmd_with_data(spi, Command::LutBlackToWhite, &LUT_BW)?;
265        self.cmd_with_data(spi, Command::LutWhiteToBlack, &LUT_WB)?;
266        self.cmd_with_data(spi, Command::LutBlackToBlack, &LUT_BB)?;
267        Ok(())
268    }
269
270    fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
271        self.interface.wait_until_idle(delay, IS_BUSY_LOW);
272        Ok(())
273    }
274}
275
276impl<SPI, BUSY, DC, RST, DELAY> WaveshareThreeColorDisplay<SPI, BUSY, DC, RST, DELAY>
277    for Epd2in7b<SPI, BUSY, DC, RST, DELAY>
278where
279    SPI: SpiDevice,
280    BUSY: InputPin,
281    DC: OutputPin,
282    RST: OutputPin,
283    DELAY: DelayNs,
284{
285    fn update_color_frame(
286        &mut self,
287        spi: &mut SPI,
288        delay: &mut DELAY,
289        black: &[u8],
290        chromatic: &[u8],
291    ) -> Result<(), SPI::Error> {
292        self.update_achromatic_frame(spi, delay, black)?;
293        self.update_chromatic_frame(spi, delay, chromatic)
294    }
295
296    /// Update only the black/white data of the display.
297    ///
298    /// Finish by calling `update_chromatic_frame`.
299    fn update_achromatic_frame(
300        &mut self,
301        spi: &mut SPI,
302        _delay: &mut DELAY,
303        achromatic: &[u8],
304    ) -> Result<(), SPI::Error> {
305        self.interface.cmd(spi, Command::DataStartTransmission1)?;
306
307        self.send_buffer_helper(spi, achromatic)?;
308
309        self.interface.cmd(spi, Command::DataStop)
310    }
311
312    /// Update only chromatic data of the display.
313    ///
314    /// This data takes precedence over the black/white data.
315    fn update_chromatic_frame(
316        &mut self,
317        spi: &mut SPI,
318        delay: &mut DELAY,
319        chromatic: &[u8],
320    ) -> Result<(), SPI::Error> {
321        self.interface.cmd(spi, Command::DataStartTransmission2)?;
322
323        self.send_buffer_helper(spi, chromatic)?;
324
325        self.interface.cmd(spi, Command::DataStop)?;
326        self.wait_until_idle(spi, delay)?;
327
328        Ok(())
329    }
330}
331
332impl<SPI, BUSY, DC, RST, DELAY> Epd2in7b<SPI, BUSY, DC, RST, DELAY>
333where
334    SPI: SpiDevice,
335    BUSY: InputPin,
336    DC: OutputPin,
337    RST: OutputPin,
338    DELAY: DelayNs,
339{
340    fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> {
341        self.interface.cmd(spi, command)
342    }
343
344    fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
345        self.interface.data(spi, data)
346    }
347
348    fn send_buffer_helper(&mut self, spi: &mut SPI, buffer: &[u8]) -> Result<(), SPI::Error> {
349        // Based on the waveshare implementation, all data for color values is flipped. This helper
350        // method makes that transmission easier
351        for b in buffer.iter() {
352            self.send_data(spi, &[!b])?;
353        }
354        Ok(())
355    }
356
357    fn cmd_with_data(
358        &mut self,
359        spi: &mut SPI,
360        command: Command,
361        data: &[u8],
362    ) -> Result<(), SPI::Error> {
363        self.interface.cmd_with_data(spi, command, data)
364    }
365
366    /// Refresh display for partial frame
367    pub fn display_partial_frame(
368        &mut self,
369        spi: &mut SPI,
370        delay: &mut DELAY,
371        x: u32,
372        y: u32,
373        width: u32,
374        height: u32,
375    ) -> Result<(), SPI::Error> {
376        self.command(spi, Command::PartialDisplayRefresh)?;
377        self.send_data(spi, &[(x >> 8) as u8])?;
378        self.send_data(spi, &[(x & 0xf8) as u8])?;
379        self.send_data(spi, &[(y >> 8) as u8])?;
380        self.send_data(spi, &[(y & 0xff) as u8])?;
381        self.send_data(spi, &[(width >> 8) as u8])?;
382        self.send_data(spi, &[(width & 0xf8) as u8])?;
383        self.send_data(spi, &[(height >> 8) as u8])?;
384        self.send_data(spi, &[(height & 0xff) as u8])?;
385        self.wait_until_idle(spi, delay)?;
386        Ok(())
387    }
388
389    /// Update black/achromatic frame
390    #[allow(clippy::too_many_arguments)]
391    pub fn update_partial_achromatic_frame(
392        &mut self,
393        spi: &mut SPI,
394        delay: &mut DELAY,
395        achromatic: &[u8],
396        x: u32,
397        y: u32,
398        width: u32,
399        height: u32,
400    ) -> Result<(), SPI::Error> {
401        self.interface
402            .cmd(spi, Command::PartialDataStartTransmission1)?;
403        self.send_data(spi, &[(x >> 8) as u8])?;
404        self.send_data(spi, &[(x & 0xf8) as u8])?;
405        self.send_data(spi, &[(y >> 8) as u8])?;
406        self.send_data(spi, &[(y & 0xff) as u8])?;
407        self.send_data(spi, &[(width >> 8) as u8])?;
408        self.send_data(spi, &[(width & 0xf8) as u8])?;
409        self.send_data(spi, &[(height >> 8) as u8])?;
410        self.send_data(spi, &[(height & 0xff) as u8])?;
411        self.wait_until_idle(spi, delay)?;
412
413        for b in achromatic.iter() {
414            // Flipping based on waveshare implementation
415            self.send_data(spi, &[!b])?;
416        }
417
418        Ok(())
419    }
420
421    /// Update partial chromatic/red frame
422    #[allow(clippy::too_many_arguments)]
423    pub fn update_partial_chromatic_frame(
424        &mut self,
425        spi: &mut SPI,
426        delay: &mut DELAY,
427        chromatic: &[u8],
428        x: u32,
429        y: u32,
430        width: u32,
431        height: u32,
432    ) -> Result<(), SPI::Error> {
433        self.interface
434            .cmd(spi, Command::PartialDataStartTransmission2)?;
435        self.send_data(spi, &[(x >> 8) as u8])?;
436        self.send_data(spi, &[(x & 0xf8) as u8])?;
437        self.send_data(spi, &[(y >> 8) as u8])?;
438        self.send_data(spi, &[(y & 0xff) as u8])?;
439        self.send_data(spi, &[(width >> 8) as u8])?;
440        self.send_data(spi, &[(width & 0xf8) as u8])?;
441        self.send_data(spi, &[(height >> 8) as u8])?;
442        self.send_data(spi, &[(height & 0xff) as u8])?;
443        self.wait_until_idle(spi, delay)?;
444
445        for b in chromatic.iter() {
446            // Flipping based on waveshare implementation
447            self.send_data(spi, &[!b])?;
448        }
449
450        Ok(())
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    #[test]
459    fn epd_size() {
460        assert_eq!(WIDTH, 176);
461        assert_eq!(HEIGHT, 264);
462        assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White);
463    }
464}