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,
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::CommandDataSend as _,
17    log::{debug, debug_assert},
18    DisplayPartial, DisplaySimple, Displayable, EpdHw, 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
182/// Low-level commands for the Epd2In9 v2 display. You probably want to use the other methods
183/// exposed on the [Epd2In9V2] for most operations, but can send commands directly with [Epd2In9V2::send] for low-level
184/// control or experimentation.
185#[cfg_attr(feature = "defmt", derive(defmt::Format))]
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub enum Command {
188    /// Used to initialise the display.
189    DriverOutputControl = 0x01,
190    /// Sets the gate driving voltage (standard value: 0x00, or 0x17).
191    SetGateDrivingVoltage = 0x03,
192    /// Sets the source driving voltage (standard value: [0x41, 0xA8, 0x32]).
193    SetSourceDrivingVoltage = 0x04,
194    /// Used to enter deep sleep mode. Requires a hardware reset and reinitialisation to wake up.
195    DeepSleepMode = 0x10,
196    /// Changes the auto-increment behaviour of the address counter.
197    DataEntryModeSetting = 0x11,
198    /// Resets all commands and parameters to default values (except deep sleep mode).
199    SwReset = 0x12,
200    /// Activates the display update sequence. This must be set beforehand using [Command::DisplayUpdateControl2].
201    /// This operation must not be interrupted.
202    MasterActivation = 0x20,
203    /// Used for a RAM "bypass" mode when using [RefreshMode::Partial]. This is poorly explained in the docs,
204    /// but essentially we have these options:
205    ///
206    /// In black and white mode:
207    ///
208    /// 1. `0x00` (default): just update the pixels that have changed **between the two internal
209    ///    frame buffers**. This normally does what you expect. You can hack it a bit to do
210    ///    interesting things by writing to both the old and new frame buffers.
211    /// 2. `0x04`: just update the white (`BinaryColor::On`) pixels in the current frame buffer. It
212    ///    doesn't matter what is in the old frame buffer.
213    /// 3. `0x08`: just update the black (`BinaryColor::Off`) pixels in the current frame buffer.
214    ///    It doesn't matter what is in the old frame buffer.
215    ///
216    /// In 4-color greyscale mode: same as above for the behaviour of the black and white bit, but
217    /// OR-ed with:
218    ///
219    /// 1. `0x00` (default)
220    /// 2. `0x40` (just update 1 bits)
221    /// 3. `0x80` (just update 0 bits)
222    ///
223    /// TODO: verify the behaviour of greyscale mode.
224    DisplayUpdateControl1 = 0x21,
225    /// Configures the display update sequence for use with [Command::MasterActivation].
226    DisplayUpdateControl2 = 0x22,
227    /// Writes low bits to the current frame buffer.
228    WriteLowRam = 0x24,
229    /// Writes high bits to the current frame buffer.
230    WriteHighRam = 0x26,
231    /// Triggers a read of the VCOM voltage. Requires that CLKEN and ANALOGEN have been enabled via
232    /// [Command::DisplayUpdateControl2].
233    ReadVcom = 0x28,
234    /// Sets the duration to hold before reading the VCOM value.
235    SetVcomReadDuration = 0x29,
236    /// Programs the VCOM register into the OTP. Requires that CLKEN has been enabled via
237    /// [Command::DisplayUpdateControl2].
238    ProgramVcomOtp = 0x2A,
239    /// Writes to the VCOM register.
240    WriteVcom = 0x2C,
241
242    /// ?? Reads OTP registers (sections: VCOM OTP selection, VCOM register, Display Mode, Waveform Version).
243    ReadOtpRegisters = 0x2D,
244    /// ?? Reads 10 byte User ID stored in OTP.
245    ReadUserId = 0x2E,
246    /// ?? Programs the OTP of Waveform Setting (requires writing the bytes into RAM first). Requires
247    /// CLKEN to have been enabled via [Command::DisplayUpdateControl2].
248    ProgramWsOtp = 0x30,
249    /// ?? Loads the OTP of Waveform Setting. Requires CLKEN to have been enabled via
250    /// [Command::DisplayUpdateControl2].
251    LoadWsOtp = 0x31,
252
253    /// Writes the LUT register (153 bytes, containing VS[nX-LUTm], TP[nX], RP[n], SR[nXY], FR[n], and XON[nXY]).
254    WriteLut = 0x32,
255
256    /// ?? Programs OTP selection according to the OTP selection control (registers 0x37 and 0x38).
257    /// Requires CLKEN to have been enabled via [Command::DisplayUpdateControl2].
258    ProgramOtpSelection = 0x36,
259
260    /// Undocumented command for writing OTP data.    
261    /// Writes the register for the user ID that can be stored in the OTP.
262    WriteRegisterForUserId = 0x38,
263    /// ?? Sets the OTP program mode:
264    ///
265    /// * 0x00: normal mode
266    /// * 0x03: internally generated OTP programming voltage
267    SetOtpProgramMode = 0x39,
268    /// Undocumented command used when initialising each refresh mode.
269    SetBorderWaveform = 0x3C,
270    /// Undocumented command needed for setting the LUT.
271    SetLutMagic = 0x3F,
272
273    /// Sets the start and end positions of the X axis for the auto-incrementing address counter.
274    /// Start and end are inclusive.
275    ///
276    /// Note that the x position can only be written on a whole byte basis (8 bits at once). The
277    /// start and end positions are therefore sent right shifted 3 bits to indicate the byte number
278    /// being written. For example, to write the first 32 x positions, you would send 0 (0 >> 3 =
279    /// 0), and 3 (31 >> 3 = 3). If you tried to write just the first 25 x positions, you would end
280    /// up sending the same values and actually writing all 32.
281    SetRamXStartEnd = 0x44,
282    /// Sets the start and end positions of the Y axis for the auto-incrementing address counter.
283    /// Start and end are inclusive.
284    SetRamYStartEnd = 0x45,
285    /// Sets the current x coordinate of the address counter.
286    /// Note that the x position can only be configured as a multiple of 8.
287    SetRamX = 0x4E,
288    /// Sets the current y coordinate of the address counter.
289    SetRamY = 0x4F,
290}
291
292impl Command {
293    /// Returns the register address for this command.
294    fn register(&self) -> u8 {
295        *self as u8
296    }
297}
298
299/// The length of the underlying buffer used by [Epd2In9V2].
300pub const BINARY_BUFFER_LENGTH: usize =
301    binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32));
302/// The buffer type used by [Epd2In9V2].
303pub type Epd2In9BinaryBuffer = BinaryBuffer<BINARY_BUFFER_LENGTH>;
304/// Constructs a new binary buffer for use with the [Epd2In9V2] display.
305pub fn new_binary_buffer() -> Epd2In9BinaryBuffer {
306    Epd2In9BinaryBuffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
307}
308pub type Epd2In9Gray2Buffer = Gray2SplitBuffer<BINARY_BUFFER_LENGTH>;
309pub fn new_gray2_buffer() -> Epd2In9Gray2Buffer {
310    Epd2In9Gray2Buffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
311}
312
313/// This should be sent with [Command::DriverOutputControl] during initialisation.
314///
315/// From the sample code, the bytes mean the following:
316///
317/// * low byte of display long edge
318/// * high byte of display long edge
319/// * GD = 0, SM = 0, TB = 0 (unclear what this means)
320const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
321
322/// Controls v2 of the 2.9" Waveshare e-paper display.
323///
324/// * [datasheet](https://files.waveshare.com/upload/7/79/2.9inch-e-paper-v2-specification.pdf)
325/// * [sample code](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in9_V2.py)
326///
327/// The display has a portrait orientation. This display supports either
328/// [embedded_graphics::pixelcolor::BinaryColor] or [embedded_graphics::pixelcolor::Gray2],
329/// depending on the display mode.
330///
331/// When using `BinaryColor`, `Off` is black and `On` is white.
332pub struct Epd2In9V2<HW, STATE>
333where
334    HW: EpdHw,
335    STATE: State,
336{
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: EpdHw,
378{
379    pub fn new(hw: HW) -> Self {
380        Epd2In9V2 {
381            hw,
382            state: StateUninitialized(),
383        }
384    }
385}
386
387pub enum Bypass {
388    /// Remove any RAM bypass setting.
389    Normal = 0,
390    /// Reads all zeros as the base for the partial diff.
391    AllZero = 0b100,
392    /// Reads the base of the partial diff as if it's inverted.
393    Inverted = 0b1000,
394}
395
396impl<HW, STATE> Epd2In9V2<HW, STATE>
397where
398    HW: EpdHw,
399    STATE: StateAwake,
400{
401    /// Initialises the display.
402    pub async fn init(
403        mut self,
404        spi: &mut HW::Spi,
405        mode: RefreshMode,
406    ) -> Result<Epd2In9V2<HW, StateReady>, HW::Error> {
407        debug!("Initialising display");
408        self = self.reset().await?;
409
410        let mut epd = Epd2In9V2 {
411            hw: self.hw,
412            state: StateReady { mode },
413        };
414
415        epd.set_refresh_mode_impl(spi, mode).await?;
416        Ok(epd)
417    }
418
419    /// Send the following command and data to the display. Waits until the display is no longer busy before sending.
420    pub async fn send(
421        &mut self,
422        spi: &mut HW::Spi,
423        command: Command,
424        data: &[u8],
425    ) -> Result<(), HW::Error> {
426        self.hw.send(spi, command.register(), data).await
427    }
428}
429
430impl<HW: EpdHw> Epd2In9V2<HW, StateReady> {
431    /// Sets the refresh mode.
432    pub async fn set_refresh_mode(
433        &mut self,
434        spi: &mut HW::Spi,
435        mode: RefreshMode,
436    ) -> Result<(), HW::Error> {
437        if self.state.mode == mode {
438            Ok(())
439        } else {
440            debug!("Changing refresh mode to {:?}", mode);
441            self.set_refresh_mode_impl(spi, mode).await?;
442            Ok(())
443        }
444    }
445
446    async fn set_refresh_mode_impl(
447        &mut self,
448        spi: &mut <HW as EpdHw>::Spi,
449        mode: RefreshMode,
450    ) -> Result<(), <HW as EpdHw>::Error> {
451        // Reset all configurations to default.
452        self.send(spi, Command::SwReset, &[]).await?;
453
454        self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
455            .await?;
456        // Auto-increment X and Y, moving in the X direction first.
457        self.send(spi, Command::DataEntryModeSetting, &[0b11])
458            .await?;
459
460        let black_and_white_byte = if mode.is_black_and_white() {
461            0x80
462        } else {
463            0x00
464        };
465        self.send(
466            spi,
467            Command::DisplayUpdateControl1,
468            &[0x00, black_and_white_byte],
469        )
470        .await?;
471
472        self.send(spi, Command::SetBorderWaveform, mode.border_waveform())
473            .await?;
474
475        self.send(spi, Command::WriteLut, mode.lut()).await?;
476        self.send(spi, Command::SetLutMagic, mode.lut_magic())
477            .await?;
478        self.send(spi, Command::SetGateDrivingVoltage, mode.gate_voltage())
479            .await?;
480        self.send(spi, Command::SetSourceDrivingVoltage, mode.source_voltage())
481            .await?;
482        self.send(spi, Command::WriteVcom, mode.vcom()).await?;
483
484        if mode == RefreshMode::Partial {
485            // Mystery undocumented command from sample code.
486            self.hw
487                .send(
488                    spi,
489                    0x37,
490                    &[0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00],
491                )
492                .await?;
493
494            self.send(spi, Command::DisplayUpdateControl2, &[0xC3])
495                .await?;
496            self.send(spi, Command::MasterActivation, &[]).await?;
497        }
498        self.state.mode = mode;
499
500        Ok(())
501    }
502
503    /// Sets the "ram bypass", which modifies what the display reads when it tries to access the
504    /// framebuffers.
505    ///
506    /// If in black and white mode, `low_bypass` corresponds with the main framebuffer, and
507    /// `high_bypass` with the diff base.
508    ///
509    /// In Gray2 mode, they represent the low and high bits.
510    pub async fn set_ram_bypass(
511        &mut self,
512        spi: &mut HW::Spi,
513        low_bypass: Bypass,
514        high_bypass: Bypass,
515    ) -> Result<(), HW::Error> {
516        let black_and_white_byte = if self.state.mode.is_black_and_white() {
517            0x80
518        } else {
519            0x00
520        };
521        self.send(
522            spi,
523            Command::DisplayUpdateControl1,
524            &[
525                ((high_bypass as u8) << 4) | (low_bypass as u8),
526                black_and_white_byte,
527            ],
528        )
529        .await
530    }
531
532    /// Sets the window to which the next image data will be written.
533    ///
534    /// The x-axis only supports multiples of 8; values outside this result in a debug-mode panic,
535    /// or potentially misaligned content when debug assertions are disabled.
536    pub async fn set_window(
537        &mut self,
538        spi: &mut HW::Spi,
539        shape: Rectangle,
540    ) -> Result<(), <HW as EpdHw>::Error> {
541        let (x_start, x_end) = if self.state.mode == RefreshMode::Gray2 {
542            // When using gray2, for some reason the position is misaligned. This fixes it.
543            let x_start = shape.top_left.x + 8;
544            let x_end = shape.top_left.x + shape.size.width as i32 + 7;
545            (x_start, x_end)
546        } else {
547            let x_start = shape.top_left.x;
548            (x_start, x_start + shape.size.width as i32 - 1)
549        };
550        // Use a debug assert as this is a soft failure in production; it will just lead to
551        // slightly misaligned display content.
552        debug_assert!(
553            x_start % 8 == 0 && x_end % 8 == 7,
554            "window's top_left.x and width must be 8-bit aligned"
555        );
556        let x_start_byte = ((x_start >> 3) & 0xFF) as u8;
557        let x_end_byte = ((x_end >> 3) & 0xFF) as u8;
558        self.send(spi, Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
559            .await?;
560
561        let (y_start_low, y_start_high) = split_low_and_high(shape.top_left.y as u16);
562        let (y_end_low, y_end_high) =
563            split_low_and_high((shape.top_left.y + shape.size.height as i32 - 1) as u16);
564        self.send(
565            spi,
566            Command::SetRamYStartEnd,
567            &[y_start_low, y_start_high, y_end_low, y_end_high],
568        )
569        .await?;
570
571        Ok(())
572    }
573
574    /// Sets the cursor position to write the next data to.
575    ///
576    /// The x-axis only supports multiples of 8; values outside this will result in a panic in
577    /// debug mode, or potentially misaligned content if debug assertions are disabled.
578    pub async fn set_cursor(
579        &mut self,
580        spi: &mut HW::Spi,
581        position: Point,
582    ) -> Result<(), <HW as EpdHw>::Error> {
583        // Use a debug assert as this is a soft failure in production; it will just lead to
584        // slightly misaligned display content.
585        debug_assert_eq!(position.x % 8, 0, "position.x must be 8-bit aligned");
586        let x_pos = if self.state.mode == RefreshMode::Gray2 {
587            position.x + 8
588        } else {
589            position.x
590        };
591
592        self.send(spi, Command::SetRamX, &[(x_pos >> 3) as u8])
593            .await?;
594        let (y_low, y_high) = split_low_and_high(position.y as u16);
595        self.send(spi, Command::SetRamY, &[y_low, y_high]).await?;
596        Ok(())
597    }
598}
599
600async fn reset_impl<HW: EpdHw>(hw: &mut HW) -> Result<(), HW::Error> {
601    debug!("Resetting EPD");
602    // Assume reset is already high.
603    hw.reset().set_low()?;
604    hw.delay().delay_ms(10).await;
605    hw.reset().set_high()?;
606    hw.delay().delay_ms(10).await;
607    Ok(())
608}
609
610impl<HW: EpdHw, STATE: StateAwake> Reset<HW::Error> for Epd2In9V2<HW, STATE> {
611    type DisplayOut = Epd2In9V2<HW, STATE>;
612
613    async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
614        reset_impl(&mut self.hw).await?;
615        Ok(self)
616    }
617}
618
619impl<HW: EpdHw, W: StateAwake> Reset<HW::Error> for Epd2In9V2<HW, StateAsleep<W>> {
620    type DisplayOut = Epd2In9V2<HW, W>;
621
622    async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
623        reset_impl(&mut self.hw).await?;
624        Ok(Epd2In9V2 {
625            hw: self.hw,
626            state: self.state.wake_state,
627        })
628    }
629}
630
631impl<HW: EpdHw, STATE: StateAwake> Sleep<HW::Spi, HW::Error> for Epd2In9V2<HW, STATE> {
632    type DisplayOut = Epd2In9V2<HW, StateAsleep<STATE>>;
633
634    async fn sleep(mut self, spi: &mut HW::Spi) -> Result<Self::DisplayOut, <HW as EpdHw>::Error> {
635        debug!("Sleeping EPD");
636        self.send(spi, Command::DeepSleepMode, &[0x01]).await?;
637        Ok(Epd2In9V2 {
638            hw: self.hw,
639            state: StateAsleep {
640                wake_state: self.state,
641            },
642        })
643    }
644}
645
646impl<HW: EpdHw, W: StateAwake> Wake<HW::Spi, HW::Error> for Epd2In9V2<HW, StateAsleep<W>> {
647    type DisplayOut = Epd2In9V2<HW, W>;
648    async fn wake(self, _spi: &mut HW::Spi) -> Result<Self::DisplayOut, <HW as EpdHw>::Error> {
649        debug!("Waking EPD");
650        self.reset().await
651    }
652}
653
654impl<HW: EpdHw> Displayable<HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady> {
655    async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), <HW as EpdHw>::Error> {
656        debug!("Updating display");
657
658        let mode = self.state.mode;
659        let update_control = mode.display_update_control_2();
660        self.send(spi, Command::DisplayUpdateControl2, update_control)
661            .await?;
662
663        self.send(spi, Command::MasterActivation, &[]).await?;
664        Ok(())
665    }
666}
667
668impl<HW: EpdHw> DisplaySimple<1, 1, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady> {
669    async fn display_framebuffer(
670        &mut self,
671        spi: &mut HW::Spi,
672        buf: &dyn BufferView<1, 1>,
673    ) -> Result<(), HW::Error> {
674        self.write_framebuffer(spi, buf).await?;
675
676        self.update_display(spi).await
677    }
678
679    async fn write_framebuffer(
680        &mut self,
681        spi: &mut HW::Spi,
682        buf: &dyn BufferView<1, 1>,
683    ) -> Result<(), HW::Error> {
684        let buffer_bounds = buf.window();
685        self.set_window(spi, buffer_bounds).await?;
686        self.set_cursor(spi, buffer_bounds.top_left).await?;
687        self.send(spi, Command::WriteLowRam, buf.data()[0]).await
688    }
689}
690
691impl<HW: EpdHw> DisplaySimple<1, 2, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady> {
692    async fn display_framebuffer(
693        &mut self,
694        spi: &mut HW::Spi,
695        buf: &dyn BufferView<1, 2>,
696    ) -> Result<(), HW::Error> {
697        self.write_framebuffer(spi, buf).await?;
698
699        self.update_display(spi).await
700    }
701
702    async fn write_framebuffer(
703        &mut self,
704        spi: &mut HW::Spi,
705        buf: &dyn BufferView<1, 2>,
706    ) -> Result<(), HW::Error> {
707        let buffer_bounds = buf.window();
708        self.set_window(spi, buffer_bounds).await?;
709        self.set_cursor(spi, buffer_bounds.top_left).await?;
710        self.send(spi, Command::WriteLowRam, buf.data()[0]).await?;
711        self.send(spi, Command::WriteHighRam, buf.data()[1]).await
712    }
713}
714
715impl<HW: EpdHw> DisplayPartial<1, 1, HW::Spi, HW::Error> for Epd2In9V2<HW, StateReady> {
716    async fn write_base_framebuffer(
717        &mut self,
718        spi: &mut HW::Spi,
719        buf: &dyn BufferView<1, 1>,
720    ) -> Result<(), HW::Error> {
721        let buffer_bounds = buf.window();
722        self.set_window(spi, buffer_bounds).await?;
723        self.set_cursor(spi, buffer_bounds.top_left).await?;
724        self.send(spi, Command::WriteHighRam, buf.data()[0]).await
725    }
726}