epd_waveshare/epd2in13bc/
mod.rs

1//! A simple Driver for the Waveshare 2.13" (B/C) E-Ink Display via SPI
2//! More information on this display can be found at the [Waveshare Wiki](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B))
3//! This driver was build and tested for 212x104, 2.13inch E-Ink display HAT for Raspberry Pi, three-color, SPI interface
4//!
5//! # Example for the 2.13" E-Ink Display
6//!
7//!```rust, no_run
8//!# use embedded_hal_mock::eh1::*;
9//!# fn main() -> Result<(), embedded_hal::spi::ErrorKind> {
10//!use embedded_graphics::{prelude::*, primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder}};
11//!use epd_waveshare::{epd2in13bc::*, prelude::*};
12//!#
13//!# let expectations = [];
14//!# let mut spi = spi::Mock::new(&expectations);
15//!# let expectations = [];
16//!# let cs_pin = digital::Mock::new(&expectations);
17//!# let busy_in = digital::Mock::new(&expectations);
18//!# let dc = digital::Mock::new(&expectations);
19//!# let rst = digital::Mock::new(&expectations);
20//!# let mut delay = delay::NoopDelay::new();
21//!
22//!// Setup EPD
23//!let mut epd = Epd2in13bc::new(&mut spi, busy_in, dc, rst, &mut delay, None)?;
24//!
25//!// Use display graphics from embedded-graphics
26//!// This display is for the black/white/chromatic pixels
27//!let mut tricolor_display = Display2in13bc::default();
28//!
29//!// Use embedded graphics for drawing a black line
30//!let _ = Line::new(Point::new(0, 120), Point::new(0, 200))
31//!    .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 1))
32//!    .draw(&mut tricolor_display);
33//!
34//!// We use `chromatic` but it will be shown as red/yellow
35//!let _ = Line::new(Point::new(15, 120), Point::new(15, 200))
36//!    .into_styled(PrimitiveStyle::with_stroke(TriColor::Chromatic, 1))
37//!    .draw(&mut tricolor_display);
38//!
39//!// Display updated frame
40//!epd.update_color_frame(
41//!    &mut spi,
42//!    &mut delay,
43//!    &tricolor_display.bw_buffer(),
44//!    &tricolor_display.chromatic_buffer()
45//!)?;
46//!epd.display_frame(&mut spi, &mut delay)?;
47//!
48//!// Set the EPD to sleep
49//!epd.sleep(&mut spi, &mut delay)?;
50//!# Ok(())
51//!# }
52//!```
53use embedded_hal::{delay::*, digital::*, spi::SpiDevice};
54
55use crate::interface::DisplayInterface;
56use crate::traits::{
57    InternalWiAdditions, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay,
58};
59
60/// Width of epd2in13bc in pixels
61pub const WIDTH: u32 = 104;
62/// Height of epd2in13bc in pixels
63pub const HEIGHT: u32 = 212;
64/// Default background color (white) of epd2in13bc display
65pub const DEFAULT_BACKGROUND_COLOR: TriColor = TriColor::White;
66
67/// Number of bits for b/w buffer and same for chromatic buffer
68const NUM_DISPLAY_BITS: u32 = WIDTH / 8 * HEIGHT;
69
70const IS_BUSY_LOW: bool = true;
71const VCOM_DATA_INTERVAL: u8 = 0x07;
72const WHITE_BORDER: u8 = 0x70;
73const BLACK_BORDER: u8 = 0x30;
74const CHROMATIC_BORDER: u8 = 0xb0;
75const FLOATING_BORDER: u8 = 0xF0;
76const SINGLE_BYTE_WRITE: bool = true;
77
78use crate::color::TriColor;
79
80pub(crate) mod command;
81use self::command::Command;
82use crate::buffer_len;
83
84/// Full size buffer for use with the 2.13" b/c EPD
85#[cfg(feature = "graphics")]
86pub type Display2in13bc = crate::graphics::Display<
87    WIDTH,
88    HEIGHT,
89    true,
90    { buffer_len(WIDTH as usize, HEIGHT as usize * 2) },
91    TriColor,
92>;
93
94/// Epd2in13bc driver
95pub struct Epd2in13bc<SPI, BUSY, DC, RST, DELAY> {
96    interface: DisplayInterface<SPI, BUSY, DC, RST, DELAY, SINGLE_BYTE_WRITE>,
97    color: TriColor,
98}
99
100impl<SPI, BUSY, DC, RST, DELAY> InternalWiAdditions<SPI, BUSY, DC, RST, DELAY>
101    for Epd2in13bc<SPI, BUSY, DC, RST, DELAY>
102where
103    SPI: SpiDevice,
104    BUSY: InputPin,
105    DC: OutputPin,
106    RST: OutputPin,
107    DELAY: DelayNs,
108{
109    fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
110        // Values taken from datasheet and sample code
111
112        self.interface.reset(delay, 10_000, 10_000);
113
114        // start the booster
115        self.interface
116            .cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x17])?;
117
118        // power on
119        self.command(spi, Command::PowerOn)?;
120        delay.delay_us(5000);
121        self.wait_until_idle(spi, delay)?;
122
123        // set the panel settings
124        self.cmd_with_data(spi, Command::PanelSetting, &[0x8F])?;
125
126        self.cmd_with_data(
127            spi,
128            Command::VcomAndDataIntervalSetting,
129            &[WHITE_BORDER | VCOM_DATA_INTERVAL],
130        )?;
131
132        // set resolution
133        self.send_resolution(spi)?;
134
135        self.cmd_with_data(spi, Command::VcmDcSetting, &[0x0A])?;
136
137        self.wait_until_idle(spi, delay)?;
138
139        Ok(())
140    }
141}
142
143impl<SPI, BUSY, DC, RST, DELAY> WaveshareThreeColorDisplay<SPI, BUSY, DC, RST, DELAY>
144    for Epd2in13bc<SPI, BUSY, DC, RST, DELAY>
145where
146    SPI: SpiDevice,
147    BUSY: InputPin,
148    DC: OutputPin,
149    RST: OutputPin,
150    DELAY: DelayNs,
151{
152    fn update_color_frame(
153        &mut self,
154        spi: &mut SPI,
155        delay: &mut DELAY,
156        black: &[u8],
157        chromatic: &[u8],
158    ) -> Result<(), SPI::Error> {
159        self.update_achromatic_frame(spi, delay, black)?;
160        self.update_chromatic_frame(spi, delay, chromatic)
161    }
162
163    /// Update only the black/white data of the display.
164    ///
165    /// Finish by calling `update_chromatic_frame`.
166    fn update_achromatic_frame(
167        &mut self,
168        spi: &mut SPI,
169        _delay: &mut DELAY,
170        black: &[u8],
171    ) -> Result<(), SPI::Error> {
172        self.interface.cmd(spi, Command::DataStartTransmission1)?;
173        self.interface.data(spi, black)?;
174        Ok(())
175    }
176
177    /// Update only chromatic data of the display.
178    ///
179    /// This data takes precedence over the black/white data.
180    fn update_chromatic_frame(
181        &mut self,
182        spi: &mut SPI,
183        delay: &mut DELAY,
184        chromatic: &[u8],
185    ) -> Result<(), SPI::Error> {
186        self.interface.cmd(spi, Command::DataStartTransmission2)?;
187        self.interface.data(spi, chromatic)?;
188
189        self.wait_until_idle(spi, delay)?;
190        Ok(())
191    }
192}
193
194impl<SPI, BUSY, DC, RST, DELAY> WaveshareDisplay<SPI, BUSY, DC, RST, DELAY>
195    for Epd2in13bc<SPI, BUSY, DC, RST, DELAY>
196where
197    SPI: SpiDevice,
198    BUSY: InputPin,
199    DC: OutputPin,
200    RST: OutputPin,
201    DELAY: DelayNs,
202{
203    type DisplayColor = TriColor;
204    fn new(
205        spi: &mut SPI,
206        busy: BUSY,
207        dc: DC,
208        rst: RST,
209        delay: &mut DELAY,
210        delay_us: Option<u32>,
211    ) -> Result<Self, SPI::Error> {
212        let interface = DisplayInterface::new(busy, dc, rst, delay_us);
213        let color = DEFAULT_BACKGROUND_COLOR;
214
215        let mut epd = Epd2in13bc { interface, color };
216
217        epd.init(spi, delay)?;
218
219        Ok(epd)
220    }
221
222    fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
223        // Section 8.2 from datasheet
224        self.interface.cmd_with_data(
225            spi,
226            Command::VcomAndDataIntervalSetting,
227            &[FLOATING_BORDER | VCOM_DATA_INTERVAL],
228        )?;
229
230        self.command(spi, Command::PowerOff)?;
231        // The example STM code from Github has a wait after PowerOff
232        self.wait_until_idle(spi, delay)?;
233
234        self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?;
235
236        Ok(())
237    }
238
239    fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
240        self.init(spi, delay)
241    }
242
243    fn set_background_color(&mut self, color: TriColor) {
244        self.color = color;
245    }
246
247    fn background_color(&self) -> &TriColor {
248        &self.color
249    }
250
251    fn width(&self) -> u32 {
252        WIDTH
253    }
254
255    fn height(&self) -> u32 {
256        HEIGHT
257    }
258
259    fn update_frame(
260        &mut self,
261        spi: &mut SPI,
262        buffer: &[u8],
263        delay: &mut DELAY,
264    ) -> Result<(), SPI::Error> {
265        self.interface.cmd(spi, Command::DataStartTransmission1)?;
266
267        self.interface.data(spi, buffer)?;
268
269        // Clear the chromatic layer
270        let color = self.color.get_byte_value();
271
272        self.interface.cmd(spi, Command::DataStartTransmission2)?;
273        self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
274
275        self.wait_until_idle(spi, delay)?;
276        Ok(())
277    }
278
279    #[allow(unused)]
280    fn update_partial_frame(
281        &mut self,
282        spi: &mut SPI,
283        delay: &mut DELAY,
284        buffer: &[u8],
285        x: u32,
286        y: u32,
287        width: u32,
288        height: u32,
289    ) -> Result<(), SPI::Error> {
290        Ok(())
291    }
292
293    fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
294        self.command(spi, Command::DisplayRefresh)?;
295
296        self.wait_until_idle(spi, delay)?;
297        Ok(())
298    }
299
300    fn update_and_display_frame(
301        &mut self,
302        spi: &mut SPI,
303        buffer: &[u8],
304        delay: &mut DELAY,
305    ) -> Result<(), SPI::Error> {
306        self.update_frame(spi, buffer, delay)?;
307        self.display_frame(spi, delay)?;
308        Ok(())
309    }
310
311    fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
312        self.send_resolution(spi)?;
313
314        let color = DEFAULT_BACKGROUND_COLOR.get_byte_value();
315
316        // Clear the black
317        self.interface.cmd(spi, Command::DataStartTransmission1)?;
318
319        self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
320
321        // Clear the chromatic
322        self.interface.cmd(spi, Command::DataStartTransmission2)?;
323        self.interface.data_x_times(spi, color, NUM_DISPLAY_BITS)?;
324
325        self.wait_until_idle(spi, delay)?;
326        Ok(())
327    }
328
329    fn set_lut(
330        &mut self,
331        _spi: &mut SPI,
332        _delay: &mut DELAY,
333        _refresh_rate: Option<RefreshLut>,
334    ) -> Result<(), SPI::Error> {
335        Ok(())
336    }
337
338    fn wait_until_idle(&mut self, _spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> {
339        self.interface.wait_until_idle(delay, IS_BUSY_LOW);
340        Ok(())
341    }
342}
343
344impl<SPI, BUSY, DC, RST, DELAY> Epd2in13bc<SPI, BUSY, DC, RST, DELAY>
345where
346    SPI: SpiDevice,
347    BUSY: InputPin,
348    DC: OutputPin,
349    RST: OutputPin,
350    DELAY: DelayNs,
351{
352    fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> {
353        self.interface.cmd(spi, command)
354    }
355
356    fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
357        self.interface.data(spi, data)
358    }
359
360    fn cmd_with_data(
361        &mut self,
362        spi: &mut SPI,
363        command: Command,
364        data: &[u8],
365    ) -> Result<(), SPI::Error> {
366        self.interface.cmd_with_data(spi, command, data)
367    }
368
369    fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> {
370        let w = self.width();
371        let h = self.height();
372
373        self.command(spi, Command::ResolutionSetting)?;
374
375        self.send_data(spi, &[w as u8])?;
376        self.send_data(spi, &[(h >> 8) as u8])?;
377        self.send_data(spi, &[h as u8])
378    }
379
380    /// Set the outer border of the display to the chosen color.
381    pub fn set_border_color(&mut self, spi: &mut SPI, color: TriColor) -> Result<(), SPI::Error> {
382        let border = match color {
383            TriColor::Black => BLACK_BORDER,
384            TriColor::White => WHITE_BORDER,
385            TriColor::Chromatic => CHROMATIC_BORDER,
386        };
387        self.cmd_with_data(
388            spi,
389            Command::VcomAndDataIntervalSetting,
390            &[border | VCOM_DATA_INTERVAL],
391        )
392    }
393}