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