epd_waveshare_async/
epd2in9_v2.rs

1use core::time::Duration;
2use embedded_graphics::{
3    prelude::{Point, Size},
4    primitives::Rectangle,
5};
6use embedded_hal::{
7    digital::{OutputPin, PinState},
8    spi::{Phase, Polarity},
9};
10use embedded_hal_async::delay::DelayNs;
11
12use crate::{
13    buffer::{
14        binary_buffer_length, split_low_and_high, BinaryBuffer, BufferView, Gray2SplitBuffer,
15    },
16    hw::{BusyHw, CommandDataSend as _, DcHw, DelayHw, ErrorHw, ResetHw, SpiHw},
17    log::{debug, debug_assert},
18    DisplayPartial, DisplaySimple, Displayable, Reset, Sleep, Wake,
19};
20
21/// LUT for a full refresh. This should be used occasionally for best display results.
22///
23/// See [RECOMMENDED_MIN_FULL_REFRESH_INTERVAL] and [RECOMMENDED_MAX_FULL_REFRESH_INTERVAL].
24const LUT_FULL_UPDATE: [u8; 153] = [
25    0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
26    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
28    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00,
29    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34    0x24, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00,
35];
36const LUT_MAGIC_FULL_UPDATE: [u8; 1] = [0x22];
37const GATE_VOLTAGE_FULL_UPDATE: [u8; 1] = [0x17];
38const SOURCE_VOLTAGE_FULL_UPDATE: [u8; 3] = [0x41, 0xAE, 0x32];
39const VCOM_FULL_UPDATE: [u8; 1] = [0x38];
40/// LUT for a partial refresh. This should be used for frequent updates, but it's recommended to
41/// perform a full refresh occasionally.
42///
43/// See [RECOMMENDED_MIN_FULL_REFRESH_INTERVAL] and [RECOMMENDED_MAX_FULL_REFRESH_INTERVAL].
44const LUT_PARTIAL_UPDATE: [u8; 153] = [
45    0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0,
46    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
47    0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
48    0x0, 0x0, 0x0, 0x0, 0x0, 0x0A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
49    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
50    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
51    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
52    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22,
53    0x22, 0x0, 0x0, 0x0,
54];
55const LUT_MAGIC_PARTIAL_UPDATE: [u8; 1] = [0x22];
56const GATE_VOLTAGE_PARTIAL_UPDATE: [u8; 1] = [0x17];
57const SOURCE_VOLTAGE_PARTIAL_UPDATE: [u8; 3] = [0x41, 0xB0, 0x32];
58const VCOM_PARTIAL_UPDATE: [u8; 1] = [0x36];
59const LUT_GRAY2: [u8; 153] = [
60    0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0x10, 0x00,
61    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
62    0x00, 0x00, 0x00, 0x00, 0x2A, 0x60, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63    0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05,
64    0x14, 0x00, 0x00, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x14, 0x00,
65    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69    0x24, 0x22, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00,
70];
71const LUT_MAGIC_GRAY2: [u8; 1] = [0x22];
72const GATE_VOLTAGE_GRAY2: [u8; 1] = [0x17];
73const SOURCE_VOLTAGE_GRAY2: [u8; 3] = [0x41, 0xAE, 0x32];
74const VCOM_GRAY2: [u8; 1] = [0x28];
75
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78/// The refresh mode for the display.
79pub enum RefreshMode {
80    /// Use the full update LUT. This is slower, but should be done occasionally to avoid ghosting.
81    ///
82    /// It's recommended to avoid full refreshes less than [RECOMMENDED_MIN_FULL_REFRESH_INTERVAL] apart,
83    /// but to do a full refresh at least every [RECOMMENDED_MAX_FULL_REFRESH_INTERVAL].
84    Full,
85    /// Uses the partial update LUT for fast refresh. A full refresh should be done occasionally to
86    /// avoid ghosting, see [RECOMMENDED_MAX_FULL_REFRESH_INTERVAL].
87    ///
88    /// This is the standard "fast" update. It diffs the current framebuffer against the
89    /// previous framebuffer, and just updates the pixels that differ.
90    Partial,
91    /// A refresh mode that supports 2-bit grayscale. Note that Waveshare calls this "Gray4", but
92    /// we use `Gray2` to align with the embedded-graphics color [embedded_graphics::pixelcolor::Gray2].
93    ///
94    /// There is no partial update version for Gray2. All updates require writing to both on-device framebuffers.
95    Gray2,
96}
97
98impl RefreshMode {
99    /// Returns the border waveform setting to use for this refresh mode.
100    pub fn border_waveform(&self) -> &[u8] {
101        match self {
102            RefreshMode::Full => &[0x05],
103            RefreshMode::Partial => &[0x80],
104            RefreshMode::Gray2 => &[0x04],
105        }
106    }
107
108    /// Returns the LUT to use for this refresh mode.
109    pub fn lut(&self) -> &[u8] {
110        match self {
111            RefreshMode::Full => &LUT_FULL_UPDATE,
112            RefreshMode::Partial => &LUT_PARTIAL_UPDATE,
113            RefreshMode::Gray2 => &LUT_GRAY2,
114        }
115    }
116
117    pub fn lut_magic(&self) -> &[u8] {
118        match self {
119            RefreshMode::Full => &LUT_MAGIC_FULL_UPDATE,
120            RefreshMode::Partial => &LUT_MAGIC_PARTIAL_UPDATE,
121            RefreshMode::Gray2 => &LUT_MAGIC_GRAY2,
122        }
123    }
124
125    pub fn gate_voltage(&self) -> &[u8] {
126        match self {
127            RefreshMode::Full => &GATE_VOLTAGE_FULL_UPDATE,
128            RefreshMode::Partial => &GATE_VOLTAGE_PARTIAL_UPDATE,
129            RefreshMode::Gray2 => &GATE_VOLTAGE_GRAY2,
130        }
131    }
132
133    pub fn source_voltage(&self) -> &[u8] {
134        match self {
135            RefreshMode::Full => &SOURCE_VOLTAGE_FULL_UPDATE,
136            RefreshMode::Partial => &SOURCE_VOLTAGE_PARTIAL_UPDATE,
137            RefreshMode::Gray2 => &SOURCE_VOLTAGE_GRAY2,
138        }
139    }
140
141    pub fn vcom(&self) -> &[u8] {
142        match self {
143            RefreshMode::Full => &VCOM_FULL_UPDATE,
144            RefreshMode::Partial => &VCOM_PARTIAL_UPDATE,
145            RefreshMode::Gray2 => &VCOM_GRAY2,
146        }
147    }
148
149    /// Returns the value to set for [Command::DisplayUpdateControl2] when using this refresh mode.
150    pub fn display_update_control_2(&self) -> &[u8] {
151        match self {
152            // We use 0xCF (similar to 0x0F in sample code) because we need to enable clock and
153            // analog. These are already enabled elsewhere in the sample code, but we do a slightly
154            // different set up.
155            RefreshMode::Partial => &[0xCF],
156            _ => &[0xC7],
157        }
158    }
159
160    /// If this refresh mode is black and white only.
161    pub fn is_black_and_white(&self) -> bool {
162        *self != RefreshMode::Gray2
163    }
164}
165
166/// The height of the display (portrait orientation).
167pub const DISPLAY_HEIGHT: u16 = 296;
168/// The width of the display (portrait orientation).
169pub const DISPLAY_WIDTH: u16 = 128;
170/// It's recommended to avoid doing a full refresh more often than this (at least on a regular basis).
171pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
172/// It's recommended to do a full refresh at least this often.
173pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
174pub const RECOMMENDED_SPI_HZ: u32 = 4_000_000; // 4 MHz
175/// Use this phase in conjunction with [RECOMMENDED_SPI_POLARITY] so that the EPD can capture data
176/// on the rising edge.
177pub const RECOMMENDED_SPI_PHASE: Phase = Phase::CaptureOnFirstTransition;
178/// Use this polarity in conjunction with [RECOMMENDED_SPI_PHASE] so that the EPD can capture data
179/// on the rising edge.
180pub const RECOMMENDED_SPI_POLARITY: Polarity = Polarity::IdleLow;
181/// The default pin state that indicates the display is busy.
182pub const DEFAULT_BUSY_WHEN: PinState = PinState::High;
183
184/// Low-level commands for the Epd2In9 v2 display. You probably want to use the other methods
185/// exposed on the [Epd2In9V2] for most operations, but can send commands directly with [Epd2In9V2::send] for low-level
186/// control or experimentation.
187#[cfg_attr(feature = "defmt", derive(defmt::Format))]
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub enum Command {
190    /// Used to initialise the display.
191    DriverOutputControl = 0x01,
192    /// Sets the gate driving voltage (standard value: 0x00, or 0x17).
193    SetGateDrivingVoltage = 0x03,
194    /// Sets the source driving voltage (standard value: [0x41, 0xA8, 0x32]).
195    SetSourceDrivingVoltage = 0x04,
196    /// Used to enter deep sleep mode. Requires a hardware reset and reinitialisation to wake up.
197    DeepSleepMode = 0x10,
198    /// Changes the auto-increment behaviour of the address counter.
199    DataEntryModeSetting = 0x11,
200    /// Resets all commands and parameters to default values (except deep sleep mode).
201    SwReset = 0x12,
202    /// Activates the display update sequence. This must be set beforehand using [Command::DisplayUpdateControl2].
203    /// This operation must not be interrupted.
204    MasterActivation = 0x20,
205    /// Used for a RAM "bypass" mode when using [RefreshMode::Partial]. This is poorly explained in the docs,
206    /// but essentially we have these options:
207    ///
208    /// In black and white mode:
209    ///
210    /// 1. `0x00` (default): just update the pixels that have changed **between the two internal
211    ///    frame buffers**. This normally does what you expect. You can hack it a bit to do
212    ///    interesting things by writing to both the old and new frame buffers.
213    /// 2. `0x04`: just update the white (`BinaryColor::On`) pixels in the current frame buffer. It
214    ///    doesn't matter what is in the old frame buffer.
215    /// 3. `0x08`: just update the black (`BinaryColor::Off`) pixels in the current frame buffer.
216    ///    It doesn't matter what is in the old frame buffer.
217    ///
218    /// In 4-color greyscale mode: same as above for the behaviour of the black and white bit, but
219    /// OR-ed with:
220    ///
221    /// 1. `0x00` (default)
222    /// 2. `0x40` (just update 1 bits)
223    /// 3. `0x80` (just update 0 bits)
224    ///
225    /// TODO: verify the behaviour of greyscale mode.
226    DisplayUpdateControl1 = 0x21,
227    /// Configures the display update sequence for use with [Command::MasterActivation].
228    DisplayUpdateControl2 = 0x22,
229    /// Writes low bits to the current frame buffer.
230    WriteLowRam = 0x24,
231    /// Writes high bits to the current frame buffer.
232    WriteHighRam = 0x26,
233    /// Triggers a read of the VCOM voltage. Requires that CLKEN and ANALOGEN have been enabled via
234    /// [Command::DisplayUpdateControl2].
235    ReadVcom = 0x28,
236    /// Sets the duration to hold before reading the VCOM value.
237    SetVcomReadDuration = 0x29,
238    /// Programs the VCOM register into the OTP. Requires that CLKEN has been enabled via
239    /// [Command::DisplayUpdateControl2].
240    ProgramVcomOtp = 0x2A,
241    /// Writes to the VCOM register.
242    WriteVcom = 0x2C,
243
244    /// ?? Reads OTP registers (sections: VCOM OTP selection, VCOM register, Display Mode, Waveform Version).
245    ReadOtpRegisters = 0x2D,
246    /// ?? Reads 10 byte User ID stored in OTP.
247    ReadUserId = 0x2E,
248    /// ?? Programs the OTP of Waveform Setting (requires writing the bytes into RAM first). Requires
249    /// CLKEN to have been enabled via [Command::DisplayUpdateControl2].
250    ProgramWsOtp = 0x30,
251    /// ?? Loads the OTP of Waveform Setting. Requires CLKEN to have been enabled via
252    /// [Command::DisplayUpdateControl2].
253    LoadWsOtp = 0x31,
254
255    /// Writes the LUT register (153 bytes, containing VS\[nX-LUTm\], TP\[nX\], RP\[n\], SR\[nXY\], FR\[n\], and XON\[nXY\]).
256    WriteLut = 0x32,
257
258    /// ?? Programs OTP selection according to the OTP selection control (registers 0x37 and 0x38).
259    /// Requires CLKEN to have been enabled via [Command::DisplayUpdateControl2].
260    ProgramOtpSelection = 0x36,
261
262    /// Undocumented command for writing OTP data.    
263    /// Writes the register for the user ID that can be stored in the OTP.
264    WriteRegisterForUserId = 0x38,
265    /// ?? Sets the OTP program mode:
266    ///
267    /// * 0x00: normal mode
268    /// * 0x03: internally generated OTP programming voltage
269    SetOtpProgramMode = 0x39,
270    /// Undocumented command used when initialising each refresh mode.
271    SetBorderWaveform = 0x3C,
272    /// Undocumented command needed for setting the LUT.
273    SetLutMagic = 0x3F,
274
275    /// Sets the start and end positions of the X axis for the auto-incrementing address counter.
276    /// Start and end are inclusive.
277    ///
278    /// Note that the x position can only be written on a whole byte basis (8 bits at once). The
279    /// start and end positions are therefore sent right shifted 3 bits to indicate the byte number
280    /// being written. For example, to write the first 32 x positions, you would send 0 (0 >> 3 =
281    /// 0), and 3 (31 >> 3 = 3). If you tried to write just the first 25 x positions, you would end
282    /// up sending the same values and actually writing all 32.
283    SetRamXStartEnd = 0x44,
284    /// Sets the start and end positions of the Y axis for the auto-incrementing address counter.
285    /// Start and end are inclusive.
286    SetRamYStartEnd = 0x45,
287    /// Sets the current x coordinate of the address counter.
288    /// Note that the x position can only be configured as a multiple of 8.
289    SetRamX = 0x4E,
290    /// Sets the current y coordinate of the address counter.
291    SetRamY = 0x4F,
292}
293
294impl Command {
295    /// Returns the register address for this command.
296    fn register(&self) -> u8 {
297        *self as u8
298    }
299}
300
301/// The length of the underlying buffer used by [Epd2In9V2].
302pub const BINARY_BUFFER_LENGTH: usize =
303    binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32));
304/// The buffer type used by [Epd2In9V2].
305pub type Epd2In9BinaryBuffer = BinaryBuffer<BINARY_BUFFER_LENGTH>;
306/// Constructs a new binary buffer for use with the [Epd2In9V2] display.
307pub fn new_binary_buffer() -> Epd2In9BinaryBuffer {
308    Epd2In9BinaryBuffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
309}
310pub type Epd2In9Gray2Buffer = Gray2SplitBuffer<BINARY_BUFFER_LENGTH>;
311pub fn new_gray2_buffer() -> Epd2In9Gray2Buffer {
312    Epd2In9Gray2Buffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
313}
314
315/// This should be sent with [Command::DriverOutputControl] during initialisation.
316///
317/// From the sample code, the bytes mean the following:
318///
319/// * low byte of display long edge
320/// * high byte of display long edge
321/// * GD = 0, SM = 0, TB = 0 (unclear what this means)
322const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
323
324/// Controls v2 of the 2.9" Waveshare e-paper display.
325///
326/// * [datasheet](https://files.waveshare.com/upload/7/79/2.9inch-e-paper-v2-specification.pdf)
327/// * [sample code](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in9_V2.py)
328///
329/// The display has a portrait orientation. This display supports either
330/// [embedded_graphics::pixelcolor::BinaryColor] or [embedded_graphics::pixelcolor::Gray2],
331/// depending on the display mode.
332///
333/// When using `BinaryColor`, `Off` is black and `On` is white.
334///
335/// HW should implement [ResetHw], [BusyHw], [DcHw], [SpiHw], [DelayHw], and [ErrorHw].
336pub struct Epd2In9V2<HW, STATE> {
337    hw: HW,
338    state: STATE,
339}
340
341trait StateInternal {}
342#[allow(private_bounds)]
343pub trait State: StateInternal {}
344pub trait StateAwake: State {}
345
346macro_rules! impl_base_state {
347    ($state:ident) => {
348        impl StateInternal for $state {}
349        impl State for $state {}
350    };
351}
352
353#[cfg_attr(feature = "defmt", derive(defmt::Format))]
354#[derive(Debug, Clone, Copy, PartialEq)]
355pub struct StateUninitialized();
356impl_base_state!(StateUninitialized);
357impl StateAwake for StateUninitialized {}
358
359#[cfg_attr(feature = "defmt", derive(defmt::Format))]
360#[derive(Debug, Clone, Copy, PartialEq)]
361pub struct StateReady {
362    mode: RefreshMode,
363}
364impl_base_state!(StateReady);
365impl StateAwake for StateReady {}
366
367#[cfg_attr(feature = "defmt", derive(defmt::Format))]
368#[derive(Debug, Clone, Copy, PartialEq)]
369pub struct StateAsleep<W: StateAwake> {
370    wake_state: W,
371}
372impl<W: StateAwake> StateInternal for StateAsleep<W> {}
373impl<W: StateAwake> State for StateAsleep<W> {}
374
375impl<HW> Epd2In9V2<HW, StateUninitialized>
376where
377    HW: BusyHw + DcHw + ResetHw + DelayHw + SpiHw + ErrorHw,
378    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
379        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
380        + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
381        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
382{
383    pub fn new(hw: HW) -> Self {
384        Epd2In9V2 {
385            hw,
386            state: StateUninitialized(),
387        }
388    }
389}
390
391pub enum Bypass {
392    /// Remove any RAM bypass setting.
393    Normal = 0,
394    /// Reads all zeros as the base for the partial diff.
395    AllZero = 0b100,
396    /// Reads the base of the partial diff as if it's inverted.
397    Inverted = 0b1000,
398}
399
400impl<HW, STATE> Epd2In9V2<HW, STATE>
401where
402    HW: BusyHw + DcHw + ResetHw + DelayHw + SpiHw + ErrorHw,
403    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
404        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
405        + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
406        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
407    STATE: StateAwake,
408{
409    /// Initialises the display.
410    pub async fn init(
411        mut self,
412        spi: &mut HW::Spi,
413        mode: RefreshMode,
414    ) -> Result<Epd2In9V2<HW, StateReady>, HW::Error> {
415        debug!("Initialising display");
416        self = self.reset().await?;
417
418        let mut epd = Epd2In9V2 {
419            hw: self.hw,
420            state: StateReady { mode },
421        };
422
423        epd.set_refresh_mode_impl(spi, mode).await?;
424        Ok(epd)
425    }
426}
427
428impl<HW, STATE> Epd2In9V2<HW, STATE>
429where
430    HW: BusyHw + DcHw + SpiHw + ErrorHw,
431    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
432        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
433        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
434    STATE: StateAwake,
435{
436    /// Send the following command and data to the display. Waits until the display is no longer busy before sending.
437    pub async fn send(
438        &mut self,
439        spi: &mut HW::Spi,
440        command: Command,
441        data: &[u8],
442    ) -> Result<(), HW::Error> {
443        self.hw.send(spi, command.register(), data).await
444    }
445}
446
447impl<HW> Epd2In9V2<HW, StateReady>
448where
449    HW: BusyHw + DcHw + SpiHw + ErrorHw,
450    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
451        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
452        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
453{
454    /// Sets the refresh mode.
455    pub async fn set_refresh_mode(
456        &mut self,
457        spi: &mut HW::Spi,
458        mode: RefreshMode,
459    ) -> Result<(), HW::Error> {
460        if self.state.mode == mode {
461            Ok(())
462        } else {
463            debug!("Changing refresh mode to {:?}", mode);
464            self.set_refresh_mode_impl(spi, mode).await?;
465            Ok(())
466        }
467    }
468
469    async fn set_refresh_mode_impl(
470        &mut self,
471        spi: &mut HW::Spi,
472        mode: RefreshMode,
473    ) -> Result<(), HW::Error> {
474        // Reset all configurations to default.
475        self.send(spi, Command::SwReset, &[]).await?;
476
477        self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
478            .await?;
479        // Auto-increment X and Y, moving in the X direction first.
480        self.send(spi, Command::DataEntryModeSetting, &[0b11])
481            .await?;
482
483        let black_and_white_byte = if mode.is_black_and_white() {
484            0x80
485        } else {
486            0x00
487        };
488        self.send(
489            spi,
490            Command::DisplayUpdateControl1,
491            &[0x00, black_and_white_byte],
492        )
493        .await?;
494
495        self.send(spi, Command::SetBorderWaveform, mode.border_waveform())
496            .await?;
497
498        self.send(spi, Command::WriteLut, mode.lut()).await?;
499        self.send(spi, Command::SetLutMagic, mode.lut_magic())
500            .await?;
501        self.send(spi, Command::SetGateDrivingVoltage, mode.gate_voltage())
502            .await?;
503        self.send(spi, Command::SetSourceDrivingVoltage, mode.source_voltage())
504            .await?;
505        self.send(spi, Command::WriteVcom, mode.vcom()).await?;
506
507        if mode == RefreshMode::Partial {
508            // Mystery undocumented command from sample code.
509            self.hw
510                .send(
511                    spi,
512                    0x37,
513                    &[0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00],
514                )
515                .await?;
516
517            self.send(spi, Command::DisplayUpdateControl2, &[0xC3])
518                .await?;
519            self.send(spi, Command::MasterActivation, &[]).await?;
520        }
521        self.state.mode = mode;
522
523        Ok(())
524    }
525
526    /// Sets the "ram bypass", which modifies what the display reads when it tries to access the
527    /// framebuffers.
528    ///
529    /// If in black and white mode, `low_bypass` corresponds with the main framebuffer, and
530    /// `high_bypass` with the diff base.
531    ///
532    /// In Gray2 mode, they represent the low and high bits.
533    pub async fn set_ram_bypass(
534        &mut self,
535        spi: &mut HW::Spi,
536        low_bypass: Bypass,
537        high_bypass: Bypass,
538    ) -> Result<(), HW::Error> {
539        let black_and_white_byte = if self.state.mode.is_black_and_white() {
540            0x80
541        } else {
542            0x00
543        };
544        self.send(
545            spi,
546            Command::DisplayUpdateControl1,
547            &[
548                ((high_bypass as u8) << 4) | (low_bypass as u8),
549                black_and_white_byte,
550            ],
551        )
552        .await
553    }
554
555    /// Sets the window to which the next image data will be written.
556    ///
557    /// The x-axis only supports multiples of 8; values outside this result in a debug-mode panic,
558    /// or potentially misaligned content when debug assertions are disabled.
559    pub async fn set_window(
560        &mut self,
561        spi: &mut HW::Spi,
562        shape: Rectangle,
563    ) -> Result<(), HW::Error> {
564        let (x_start, x_end) = if self.state.mode == RefreshMode::Gray2 {
565            // When using gray2, for some reason the position is misaligned. This fixes it.
566            let x_start = shape.top_left.x + 8;
567            let x_end = shape.top_left.x + shape.size.width as i32 + 7;
568            (x_start, x_end)
569        } else {
570            let x_start = shape.top_left.x;
571            (x_start, x_start + shape.size.width as i32 - 1)
572        };
573        // Use a debug assert as this is a soft failure in production; it will just lead to
574        // slightly misaligned display content.
575        debug_assert!(
576            x_start % 8 == 0 && x_end % 8 == 7,
577            "window's top_left.x and width must be 8-bit aligned"
578        );
579        let x_start_byte = ((x_start >> 3) & 0xFF) as u8;
580        let x_end_byte = ((x_end >> 3) & 0xFF) as u8;
581        self.send(spi, Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
582            .await?;
583
584        let (y_start_low, y_start_high) = split_low_and_high(shape.top_left.y as u16);
585        let (y_end_low, y_end_high) =
586            split_low_and_high((shape.top_left.y + shape.size.height as i32 - 1) as u16);
587        self.send(
588            spi,
589            Command::SetRamYStartEnd,
590            &[y_start_low, y_start_high, y_end_low, y_end_high],
591        )
592        .await?;
593
594        Ok(())
595    }
596
597    /// Sets the cursor position to write the next data to.
598    ///
599    /// The x-axis only supports multiples of 8; values outside this will result in a panic in
600    /// debug mode, or potentially misaligned content if debug assertions are disabled.
601    pub async fn set_cursor(
602        &mut self,
603        spi: &mut HW::Spi,
604        position: Point,
605    ) -> Result<(), HW::Error> {
606        // Use a debug assert as this is a soft failure in production; it will just lead to
607        // slightly misaligned display content.
608        debug_assert_eq!(position.x % 8, 0, "position.x must be 8-bit aligned");
609        let x_pos = if self.state.mode == RefreshMode::Gray2 {
610            position.x + 8
611        } else {
612            position.x
613        };
614
615        self.send(spi, Command::SetRamX, &[(x_pos >> 3) as u8])
616            .await?;
617        let (y_low, y_high) = split_low_and_high(position.y as u16);
618        self.send(spi, Command::SetRamY, &[y_low, y_high]).await?;
619        Ok(())
620    }
621}
622
623async fn reset_impl<HW>(hw: &mut HW) -> Result<(), HW::Error>
624where
625    HW: ResetHw + DelayHw + ErrorHw,
626    HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
627{
628    debug!("Resetting EPD");
629    // Assume reset is already high.
630    hw.reset().set_low()?;
631    hw.delay().delay_ms(10).await;
632    hw.reset().set_high()?;
633    hw.delay().delay_ms(10).await;
634    Ok(())
635}
636
637impl<HW, STATE: StateAwake> Reset<HW::Error> for Epd2In9V2<HW, STATE>
638where
639    HW: ResetHw + DelayHw + ErrorHw,
640    HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
641{
642    type DisplayOut = Epd2In9V2<HW, STATE>;
643
644    async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
645        reset_impl(&mut self.hw).await?;
646        Ok(self)
647    }
648}
649
650impl<HW, W: StateAwake> Reset<HW::Error> for Epd2In9V2<HW, StateAsleep<W>>
651where
652    HW: ResetHw + DelayHw + ErrorHw,
653    HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
654{
655    type DisplayOut = Epd2In9V2<HW, W>;
656
657    async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
658        reset_impl(&mut self.hw).await?;
659        Ok(Epd2In9V2 {
660            hw: self.hw,
661            state: self.state.wake_state,
662        })
663    }
664}
665
666impl<HW, STATE: StateAwake> Sleep<HW::Spi, HW::Error> for Epd2In9V2<HW, STATE>
667where
668    HW: BusyHw + DcHw + SpiHw + ErrorHw,
669    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
670        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
671        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
672{
673    type DisplayOut = Epd2In9V2<HW, StateAsleep<STATE>>;
674
675    async fn sleep(mut self, spi: &mut HW::Spi) -> Result<Self::DisplayOut, HW::Error> {
676        debug!("Sleeping EPD");
677        self.send(spi, Command::DeepSleepMode, &[0x01]).await?;
678        Ok(Epd2In9V2 {
679            hw: self.hw,
680            state: StateAsleep {
681                wake_state: self.state,
682            },
683        })
684    }
685}
686
687impl<HW, W: StateAwake> Wake<HW::Spi, HW::Error> for Epd2In9V2<HW, StateAsleep<W>>
688where
689    HW: BusyHw + DcHw + ResetHw + DelayHw + SpiHw + ErrorHw,
690    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
691        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
692        + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
693        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
694{
695    type DisplayOut = Epd2In9V2<HW, W>;
696    async fn wake(self, _spi: &mut HW::Spi) -> Result<Self::DisplayOut, HW::Error> {
697        debug!("Waking EPD");
698        self.reset().await
699    }
700}
701
702impl<HW> Displayable<HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady>
703where
704    HW: BusyHw + DcHw + SpiHw + ErrorHw,
705    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
706        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
707        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
708{
709    async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error> {
710        debug!("Updating display");
711
712        let mode = self.state.mode;
713        let update_control = mode.display_update_control_2();
714        self.send(spi, Command::DisplayUpdateControl2, update_control)
715            .await?;
716
717        self.send(spi, Command::MasterActivation, &[]).await?;
718        Ok(())
719    }
720}
721
722impl<HW> DisplaySimple<1, 1, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady>
723where
724    HW: BusyHw + DcHw + SpiHw + ErrorHw,
725    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
726        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
727        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
728{
729    async fn display_framebuffer(
730        &mut self,
731        spi: &mut HW::Spi,
732        buf: &dyn BufferView<1, 1>,
733    ) -> Result<(), HW::Error> {
734        self.write_framebuffer(spi, buf).await?;
735
736        self.update_display(spi).await
737    }
738
739    async fn write_framebuffer(
740        &mut self,
741        spi: &mut HW::Spi,
742        buf: &dyn BufferView<1, 1>,
743    ) -> Result<(), HW::Error> {
744        let buffer_bounds = buf.window();
745        self.set_window(spi, buffer_bounds).await?;
746        self.set_cursor(spi, buffer_bounds.top_left).await?;
747        self.send(spi, Command::WriteLowRam, buf.data()[0]).await
748    }
749}
750
751impl<HW> DisplaySimple<1, 2, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady>
752where
753    HW: BusyHw + DcHw + SpiHw + ErrorHw,
754    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
755        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
756        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
757{
758    async fn display_framebuffer(
759        &mut self,
760        spi: &mut HW::Spi,
761        buf: &dyn BufferView<1, 2>,
762    ) -> Result<(), HW::Error> {
763        self.write_framebuffer(spi, buf).await?;
764
765        self.update_display(spi).await
766    }
767
768    async fn write_framebuffer(
769        &mut self,
770        spi: &mut HW::Spi,
771        buf: &dyn BufferView<1, 2>,
772    ) -> Result<(), HW::Error> {
773        let buffer_bounds = buf.window();
774        self.set_window(spi, buffer_bounds).await?;
775        self.set_cursor(spi, buffer_bounds.top_left).await?;
776        self.send(spi, Command::WriteLowRam, buf.data()[0]).await?;
777        self.send(spi, Command::WriteHighRam, buf.data()[1]).await
778    }
779}
780
781impl<HW> DisplayPartial<1, 1, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady>
782where
783    HW: BusyHw + DcHw + SpiHw + ErrorHw,
784    HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
785        + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
786        + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
787{
788    async fn write_base_framebuffer(
789        &mut self,
790        spi: &mut HW::Spi,
791        buf: &dyn BufferView<1, 1>,
792    ) -> Result<(), HW::Error> {
793        let buffer_bounds = buf.window();
794        self.set_window(spi, buffer_bounds).await?;
795        self.set_cursor(spi, buffer_bounds.top_left).await?;
796        self.send(spi, Command::WriteHighRam, buf.data()[0]).await
797    }
798}