epd_waveshare/epd1in54/
mod.rs

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