1use core::time::Duration;
2use embedded_graphics::{geometry::Point, prelude::Size, primitives::Rectangle};
3use embedded_hal::{
4 digital::{OutputPin, PinState},
5 spi::{Phase, Polarity},
6};
7use embedded_hal_async::delay::DelayNs;
8
9use crate::{
10 buffer::{binary_buffer_length, BinaryBuffer, BufferView, Gray2SplitBuffer},
11 hw::{BusyHw, BusyWait, CommandDataSend, DcHw, DelayHw, ErrorHw, ResetHw, SpiHw},
12 log::debug,
13 Clear, DisplayPartial, DisplaySimple, Displayable, Reset, Sleep,
14};
15
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum RefreshMode {
20 Fast,
26 Full,
31 Partial,
38 Gray2,
43}
44
45impl RefreshMode {
46 pub fn is_black_and_white(&self) -> bool {
48 *self != RefreshMode::Gray2
49 }
50}
51
52pub const DISPLAY_WIDTH: u32 = 800;
54pub const DISPLAY_HEIGHT: u32 = 480;
56pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
58pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
60pub const RECOMMENDED_SPI_HZ: u32 = 4_000_000; pub const RECOMMENDED_SPI_PHASE: Phase = Phase::CaptureOnFirstTransition;
64pub const RECOMMENDED_SPI_POLARITY: Polarity = Polarity::IdleLow;
67pub const DEFAULT_BUSY_WHEN: PinState = PinState::Low;
69
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum Command {
76 PanelSet = 0x00,
77 PowerSet = 0x01,
78 PowerOff = 0x02,
79 PowerOffSequenceSet = 0x03,
80 PowerOn = 0x04,
81 PowerOnMeasure = 0x05,
82 BoosterSoftStart = 0x06,
83 DeepSleep = 0x07,
84 DisplayStartTrans1 = 0x10, DataStop = 0x11,
86 DisplayRefresh = 0x12,
87 DisplayStartTrans2 = 0x13, DualSPI = 0x15,
89 AutoSequence = 0x17,
90 KWLUTOption = 0x2B,
91 PLLControl = 0x30,
92 TempSensorCalibration = 0x40,
93 TempSensorSelect = 0x41,
94 TempSensorWrite = 0x42,
95 TempSensorRead = 0x43,
96 PanelBreakCheck = 0x44,
97 VCOMDataInterval = 0x50,
98 LowerPowerDetection = 0x51,
99 EndVoltageSet = 0x52,
100 TCONSet = 0x60,
101 ResolutionSet = 0x61,
102 GateSourceStartSet = 0x65,
103 Revision = 0x70,
104 GetStatus = 0x71,
105 AutoMeasurementVCOM = 0x80,
106 ReadVCOM = 0x81,
107 VCOMDCSetting = 0x82,
108 PartialWindow = 0x90,
109 PartialIn = 0x91,
110 PartialOut = 0x92,
111 ProgramMode = 0xA0,
112 ActiveProgramming = 0xA1,
113 ReadOTP = 0xA2,
114 CascadeSet = 0xE0,
115 PowerSaving = 0xE3,
116 LVDVoltageSelect = 0xE4,
117 ForceTemperature = 0xE5,
118 TempBoundaryPhaseC2 = 0xE7,
119}
120
121impl Command {
122 fn register(&self) -> u8 {
124 *self as u8
125 }
126}
127
128pub const BINARY_BUFFER_LENGTH: usize =
130 binary_buffer_length(Size::new(DISPLAY_WIDTH, DISPLAY_HEIGHT));
131pub type Epd7In5BinaryBuffer = BinaryBuffer<BINARY_BUFFER_LENGTH>;
133pub const fn new_binary_buffer() -> Epd7In5BinaryBuffer {
135 Epd7In5BinaryBuffer::new(Size::new(DISPLAY_WIDTH, DISPLAY_HEIGHT))
136}
137pub type Epd7In5Gray2Buffer = Gray2SplitBuffer<BINARY_BUFFER_LENGTH>;
138pub const fn new_gray2_buffer() -> Epd7In5Gray2Buffer {
139 Epd7In5Gray2Buffer::new(Size::new(DISPLAY_WIDTH, DISPLAY_HEIGHT))
140}
141
142pub struct Epd7In5V2<HW, STATE> {
155 hw: HW,
156 state: STATE,
157}
158
159trait StateInternal {}
160#[allow(private_bounds)]
161pub trait State: StateInternal {}
162pub trait StateAwake: State {}
163
164macro_rules! impl_base_state {
165 ($state:ident) => {
166 impl StateInternal for $state {}
167 impl State for $state {}
168 };
169}
170
171#[cfg_attr(feature = "defmt", derive(defmt::Format))]
172#[derive(Debug, Clone, Copy, PartialEq)]
173pub struct StateUninitialized();
174impl_base_state!(StateUninitialized);
175impl StateAwake for StateUninitialized {}
176
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178#[derive(Debug, Clone, Copy, PartialEq)]
179pub struct StateReady {
180 mode: RefreshMode,
181}
182impl_base_state!(StateReady);
183impl StateAwake for StateReady {}
184
185#[cfg_attr(feature = "defmt", derive(defmt::Format))]
186#[derive(Debug, Clone, Copy, PartialEq)]
187pub struct StateAsleep<W: StateAwake> {
188 wake_state: W,
189}
190impl<W: StateAwake> StateInternal for StateAsleep<W> {}
191impl<W: StateAwake> State for StateAsleep<W> {}
192
193impl<HW> Epd7In5V2<HW, StateUninitialized>
194where
195 HW: BusyHw + DcHw + ResetHw + DelayHw + SpiHw + ErrorHw,
196 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
197 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
198 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
199 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
200{
201 pub fn new(hw: HW) -> Self {
202 Epd7In5V2 {
203 hw,
204 state: StateUninitialized(),
205 }
206 }
207}
208
209impl<HW, STATE> Epd7In5V2<HW, STATE>
210where
211 HW: BusyHw + DcHw + ResetHw + DelayHw + SpiHw + ErrorHw,
212 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
213 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
214 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
215 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
216 STATE: StateAwake,
217{
218 pub async fn init(
220 mut self,
221 spi: &mut HW::Spi,
222 mode: RefreshMode,
223 ) -> Result<Epd7In5V2<HW, StateReady>, HW::Error> {
224 debug!("Initializing display to {}", mode);
225 self = self.reset().await?;
226
227 let mut epd = Epd7In5V2 {
228 hw: self.hw,
229 state: StateReady { mode },
230 };
231
232 epd.set_refresh_mode_impl(spi, mode).await?;
233 Ok(epd)
234 }
235}
236
237impl<HW, STATE> Epd7In5V2<HW, STATE>
238where
239 HW: BusyHw + DcHw + SpiHw + ErrorHw,
240 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
241 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
242 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
243 STATE: StateAwake,
244{
245 pub async fn send(
247 &mut self,
248 spi: &mut HW::Spi,
249 command: Command,
250 data: &[u8],
251 ) -> Result<(), HW::Error> {
252 self.hw.send(spi, command.register(), data).await
253 }
254}
255
256impl<HW> Epd7In5V2<HW, StateReady>
257where
258 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw + ResetHw,
259 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
260 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
261 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>
262 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
263{
264 pub async fn set_refresh_mode(
266 &mut self,
267 spi: &mut HW::Spi,
268 mode: RefreshMode,
269 ) -> Result<(), HW::Error> {
270 if self.state.mode == mode {
271 Ok(())
272 } else {
273 debug!("Changing refresh mode to {:?}", mode);
274 reset_impl(&mut self.hw).await?;
275 self.set_refresh_mode_impl(spi, mode).await?;
276 Ok(())
277 }
278 }
279
280 async fn set_refresh_mode_impl(
281 &mut self,
282 spi: &mut HW::Spi,
283 mode: RefreshMode,
284 ) -> Result<(), HW::Error> {
285 match mode {
286 RefreshMode::Fast => {
287 self.send(spi, Command::PanelSet, &[0x1F]).await?;
289
290 self.send(spi, Command::VCOMDataInterval, &[0x10, 0x07])
292 .await?;
293
294 self.send(spi, Command::PowerOn, &[]).await?;
298 self.hw.delay().delay_ms(100).await;
299 self.hw.wait_if_busy().await?;
300
301 self.send(spi, Command::BoosterSoftStart, &[0x27, 0x27, 0x18, 0x17])
303 .await?;
304 self.send(spi, Command::CascadeSet, &[0x02]).await?;
305 self.send(spi, Command::ForceTemperature, &[0x5A]).await?;
306 }
307 RefreshMode::Full => {
308 self.send(spi, Command::PowerSet, &[0x07, 0x07, 0x3F, 0x3F])
310 .await?;
311
312 self.send(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x28, 0x17])
314 .await?;
315
316 self.send(spi, Command::PowerOn, &[]).await?;
318 self.hw.delay().delay_ms(100).await;
319 self.hw.wait_if_busy().await?;
320
321 self.send(spi, Command::PanelSet, &[0x1F]).await?;
323
324 self.send(spi, Command::ResolutionSet, &[0x03, 0x20, 0x01, 0xE0])
326 .await?;
327
328 self.send(spi, Command::DualSPI, &[0x00]).await?;
330
331 self.send(spi, Command::VCOMDataInterval, &[0x10, 0x07])
333 .await?;
334
335 self.send(spi, Command::TCONSet, &[0x22]).await?;
339 }
340 RefreshMode::Partial => {
341 self.send(spi, Command::PanelSet, &[0x1F]).await?;
343
344 self.send(spi, Command::PowerOn, &[]).await?;
346 self.hw.delay().delay_ms(100).await;
347 self.hw.wait_if_busy().await?;
348
349 self.send(spi, Command::CascadeSet, &[0x02]).await?;
351 self.send(spi, Command::ForceTemperature, &[0x6E]).await?;
352 }
353 RefreshMode::Gray2 => {
354 self.send(spi, Command::PanelSet, &[0x1F]).await?;
356
357 self.send(spi, Command::VCOMDataInterval, &[0x10, 0x07])
359 .await?;
360
361 self.send(spi, Command::PowerOn, &[]).await?;
363 self.hw.delay().delay_ms(100).await;
364 self.hw.wait_if_busy().await?;
365
366 self.send(spi, Command::BoosterSoftStart, &[0x27, 0x27, 0x18, 0x17])
368 .await?;
369 self.send(spi, Command::CascadeSet, &[0x02]).await?;
370 self.send(spi, Command::ForceTemperature, &[0x5F]).await?;
371 }
372 }
373
374 self.state.mode = mode;
375
376 Ok(())
377 }
378
379 pub async fn set_window(
381 &mut self,
382 spi: &mut HW::Spi,
383 shape: Rectangle,
384 ) -> Result<(), HW::Error> {
385 let Point {
386 x: x_start,
387 y: y_start,
388 } = shape.top_left;
389 let Point { x: x_end, y: y_end } = shape.bottom_right().unwrap();
390
391 self.send(spi, Command::PartialIn, &[]).await?;
392
393 let window: [u8; _] = [
394 (x_start / 256) as u8,
395 (x_start % 256) as u8,
396 (x_end / 256) as u8,
397 (x_end % 256) as u8 - 1,
398 (y_start / 256) as u8,
399 (y_start % 256) as u8,
400 (y_end / 256) as u8,
401 (y_end % 256) as u8 - 1,
402 0x01,
403 ];
404
405 self.send(spi, Command::PartialWindow, &window).await?;
406
407 Ok(())
408 }
409}
410
411async fn reset_impl<HW>(hw: &mut HW) -> Result<(), HW::Error>
412where
413 HW: ResetHw + DelayHw + ErrorHw,
414 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
415{
416 debug!("Resetting EPD");
417 hw.reset().set_high()?;
418 hw.delay().delay_ms(20).await;
419 hw.reset().set_low()?;
420 hw.delay().delay_ms(2).await;
421 hw.reset().set_high()?;
422 hw.delay().delay_ms(200).await;
423 Ok(())
424}
425
426impl<HW, STATE: StateAwake> Reset<HW::Error> for Epd7In5V2<HW, STATE>
427where
428 HW: ResetHw + DelayHw + ErrorHw,
429 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
430{
431 type DisplayOut = Epd7In5V2<HW, STATE>;
432
433 async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
434 reset_impl(&mut self.hw).await?;
435 Ok(self)
436 }
437}
438
439impl<HW, W: StateAwake> Reset<HW::Error> for Epd7In5V2<HW, StateAsleep<W>>
440where
441 HW: ResetHw + DelayHw + ErrorHw,
442 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
443{
444 type DisplayOut = Epd7In5V2<HW, W>;
445
446 async fn reset(self) -> Result<Self::DisplayOut, HW::Error> {
447 Ok(Epd7In5V2 {
449 hw: self.hw,
450 state: self.state.wake_state,
451 })
452 }
453}
454
455impl<HW, STATE: StateAwake> Sleep<HW::Spi, HW::Error> for Epd7In5V2<HW, STATE>
456where
457 HW: BusyHw + DcHw + SpiHw + ErrorHw,
458 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
459 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
460 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
461{
462 type DisplayOut = Epd7In5V2<HW, StateAsleep<StateUninitialized>>;
463
464 async fn sleep(mut self, spi: &mut HW::Spi) -> Result<Self::DisplayOut, HW::Error> {
465 debug!("Sleeping EPD");
466 self.send(spi, Command::VCOMDataInterval, &[0xF7]).await?;
467 self.send(spi, Command::PowerOff, &[]).await?;
468 self.send(spi, Command::DeepSleep, &[0xA5]).await?;
469 Ok(Epd7In5V2 {
470 hw: self.hw,
471 state: StateAsleep {
472 wake_state: StateUninitialized(),
473 },
474 })
475 }
476}
477
478impl<HW> Displayable<HW::Spi, HW::Error> for Epd7In5V2<HW, StateReady>
479where
480 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw,
481 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
482 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
483 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
484{
485 async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error> {
486 debug!("Updating display");
487
488 self.send(spi, Command::DisplayRefresh, &[]).await?;
489 self.hw.delay().delay_ms(100).await;
490 self.hw.wait_if_busy().await?;
491 Ok(())
492 }
493}
494
495impl<HW> Clear<HW::Spi, HW::Error> for Epd7In5V2<HW, StateReady>
496where
497 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw,
498 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
499 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
500 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
501{
502 async fn clear(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error> {
503 let buf1_value;
504 let buf2_value;
505 match self.state.mode {
506 RefreshMode::Fast | RefreshMode::Full | RefreshMode::Partial => {
507 buf1_value = 0xFF;
508 buf2_value = 0x00;
509 }
510 RefreshMode::Gray2 => {
511 buf1_value = 0x00;
512 buf2_value = 0x00;
513 }
514 };
515
516 self.hw
517 .send_iter(
518 spi,
519 Command::DisplayStartTrans1 as u8,
520 Some(core::iter::repeat_n(buf1_value, BINARY_BUFFER_LENGTH)),
521 )
522 .await?;
523 self.hw
524 .send_iter(
525 spi,
526 Command::DisplayStartTrans2 as u8,
527 Some(core::iter::repeat_n(buf2_value, BINARY_BUFFER_LENGTH)),
528 )
529 .await?;
530
531 self.update_display(spi).await?;
532 Ok(())
533 }
534}
535
536impl<HW> DisplaySimple<1, 1, HW::Spi, HW::Error> for Epd7In5V2<HW, StateReady>
537where
538 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw,
539 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
540 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
541 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
542{
543 async fn display_framebuffer(
544 &mut self,
545 spi: &mut HW::Spi,
546 buf: &dyn BufferView<1, 1>,
547 ) -> Result<(), HW::Error> {
548 self.write_framebuffer(spi, buf).await?;
549
550 self.update_display(spi).await
551 }
552
553 async fn write_framebuffer(
554 &mut self,
555 spi: &mut HW::Spi,
556 buf: &dyn BufferView<1, 1>,
557 ) -> Result<(), HW::Error> {
558 let data = buf.data()[0];
559 self.send(spi, Command::DisplayStartTrans1, data).await?;
560 self.hw
561 .send_iter(
562 spi,
563 Command::DisplayStartTrans2 as u8,
564 Some(data.iter().map(|px| !px)),
565 )
566 .await?;
567 Ok(())
568 }
569}
570
571impl<HW> DisplaySimple<1, 2, HW::Spi, HW::Error> for Epd7In5V2<HW, StateReady>
572where
573 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw,
574 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
575 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
576 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
577{
578 async fn display_framebuffer(
579 &mut self,
580 spi: &mut HW::Spi,
581 buf: &dyn BufferView<1, 2>,
582 ) -> Result<(), HW::Error> {
583 self.write_framebuffer(spi, buf).await?;
584
585 self.update_display(spi).await
586 }
587
588 async fn write_framebuffer(
589 &mut self,
590 spi: &mut HW::Spi,
591 buf: &dyn BufferView<1, 2>,
592 ) -> Result<(), HW::Error> {
593 let data = buf.data();
594 self.send(spi, Command::DisplayStartTrans1, data[0]).await?;
595 self.send(spi, Command::DisplayStartTrans2, data[1]).await?;
596 Ok(())
597 }
598}
599
600impl<HW> DisplayPartial<1, 1, HW::Spi, HW::Error> for Epd7In5V2<HW, StateReady>
601where
602 HW: BusyHw + DcHw + SpiHw + ErrorHw + DelayHw + ResetHw,
603 HW::Error: From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
604 + From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
605 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>
606 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
607{
608 async fn write_base_framebuffer(
609 &mut self,
610 spi: &mut HW::Spi,
611 buf: &dyn BufferView<1, 1>,
612 ) -> Result<(), HW::Error> {
613 let buffer_bounds = buf.window();
614
615 let data = buf.data()[0];
616
617 self.send(spi, Command::VCOMDataInterval, &[0xA9, 0x07])
618 .await?;
619
620 self.set_window(spi, buffer_bounds).await?;
621
622 self.send(spi, Command::DisplayStartTrans2, data).await?;
623
624 Ok(())
625 }
626}