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 ];
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)]
88pub enum RefreshMode {
90 Full,
96 FullSlow,
102 Partial,
108 Gray2,
113}
114
115impl RefreshMode {
116 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 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 pub fn display_update_control_2(&self) -> &[u8] {
174 match self {
175 RefreshMode::Partial => &[0xCF],
179 _ => &[0xC7],
180 }
181 }
182
183 pub fn is_black_and_white(&self) -> bool {
185 *self != RefreshMode::Gray2
186 }
187}
188
189pub const DISPLAY_HEIGHT: u16 = 296;
191pub const DISPLAY_WIDTH: u16 = 128;
193pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
195pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
197pub const RECOMMENDED_SPI_HZ: u32 = 4_000_000; pub const RECOMMENDED_SPI_PHASE: Phase = Phase::CaptureOnFirstTransition;
201pub const RECOMMENDED_SPI_POLARITY: Polarity = Polarity::IdleLow;
204pub const DEFAULT_BUSY_WHEN: PinState = PinState::High;
206
207#[cfg_attr(feature = "defmt", derive(defmt::Format))]
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub enum Command {
213 DriverOutputControl = 0x01,
215 SetGateDrivingVoltage = 0x03,
217 SetSourceDrivingVoltage = 0x04,
219 DeepSleepMode = 0x10,
221 DataEntryModeSetting = 0x11,
223 SwReset = 0x12,
225 MasterActivation = 0x20,
228 DisplayUpdateControl1 = 0x21,
250 DisplayUpdateControl2 = 0x22,
252 WriteLowRam = 0x24,
254 WriteHighRam = 0x26,
256 ReadVcom = 0x28,
259 SetVcomReadDuration = 0x29,
261 ProgramVcomOtp = 0x2A,
264 WriteVcom = 0x2C,
266
267 ReadOtpRegisters = 0x2D,
269 ReadUserId = 0x2E,
271 ProgramWsOtp = 0x30,
274 LoadWsOtp = 0x31,
277
278 WriteLut = 0x32,
280
281 ProgramOtpSelection = 0x36,
284
285 WriteRegisterForUserId = 0x38,
288 SetOtpProgramMode = 0x39,
293 SetBorderWaveform = 0x3C,
295 SetLutMagic = 0x3F,
297
298 SetRamXStartEnd = 0x44,
307 SetRamYStartEnd = 0x45,
310 SetRamX = 0x4E,
313 SetRamY = 0x4F,
315}
316
317impl Command {
318 fn register(&self) -> u8 {
320 *self as u8
321 }
322}
323
324pub const BINARY_BUFFER_LENGTH: usize =
326 binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32));
327pub type Epd2In9BinaryBuffer = BinaryBuffer<BINARY_BUFFER_LENGTH>;
329pub 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
338const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
346
347pub 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 Normal = 0,
417 AllZero = 0b100,
419 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 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 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 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 self.send(spi, Command::SwReset, &[]).await?;
499
500 self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
501 .await?;
502 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 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 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 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 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 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 pub async fn set_cursor(
627 &mut self,
628 spi: &mut HW::Spi,
629 position: Point,
630 ) -> Result<(), HW::Error> {
631 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 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}