1use core::time::Duration;
2use embedded_graphics::{
3 pixelcolor::BinaryColor,
4 prelude::{Point, Size},
5 primitives::Rectangle,
6};
7use embedded_hal::{
8 digital::{OutputPin, PinState},
9 spi::{Phase, Polarity},
10};
11use embedded_hal_async::delay::DelayNs;
12
13use crate::{
14 buffer::{binary_buffer_length, split_low_and_high, BinaryBuffer, BufferView},
15 hw::{BusyHw, DcHw, DelayHw, ErrorHw, ResetHw, SpiHw},
16 log::{debug, debug_assert},
17 DisplayPartial, DisplaySimple, Displayable, Reset, Sleep, Wake,
18};
19
20const LUT_FULL_UPDATE: [u8; 30] = [
24 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26];
27const LUT_PARTIAL_UPDATE: [u8; 30] = [
32 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34];
35
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum RefreshMode {
40 Full,
45 Partial,
51 PartialBlackBypass,
54 PartialWhiteBypass,
57}
58
59impl RefreshMode {
60 pub fn lut(&self) -> &[u8; 30] {
62 match self {
63 RefreshMode::Full => &LUT_FULL_UPDATE,
64 _ => &LUT_PARTIAL_UPDATE,
65 }
66 }
67}
68
69pub const DISPLAY_HEIGHT: u16 = 296;
71pub const DISPLAY_WIDTH: u16 = 128;
73pub const RECOMMENDED_MIN_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(180);
75pub const RECOMMENDED_MAX_FULL_REFRESH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
77pub const RECOMMENDED_SPI_HZ: u32 = 4_000_000; pub const RECOMMENDED_SPI_PHASE: Phase = Phase::CaptureOnFirstTransition;
81pub const RECOMMENDED_SPI_POLARITY: Polarity = Polarity::IdleLow;
84pub const DEFAULT_BUSY_WHEN: PinState = PinState::High;
89
90#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum Command {
96 DriverOutputControl = 0x01,
98 BoosterSoftStartControl = 0x0C,
100 DeepSleepMode = 0x10,
102 DataEntryModeSetting = 0x11,
104 SwReset = 0x12,
106 TemperatureSensorControl = 0x1A,
108 MasterActivation = 0x20,
111 DisplayUpdateControl1 = 0x21,
124 DisplayUpdateControl2 = 0x22,
126 WriteRam = 0x24,
128 WriteOldRam = 0x26,
130 WriteVcom = 0x2C,
132 WriteLut = 0x32,
134 SetDummyLinePeriod = 0x3A,
136 SetGateLineWidth = 0x3B,
138 BorderWaveformControl = 0x3C,
140 SetRamXStartEnd = 0x44,
149 SetRamYStartEnd = 0x45,
152 SetRamX = 0x4E,
155 SetRamY = 0x4F,
157 Noop = 0xFF,
159}
160
161impl Command {
162 fn register(&self) -> u8 {
164 *self as u8
165 }
166}
167
168pub const BINARY_BUFFER_LENGTH: usize =
170 binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32));
171pub type Epd2In9Buffer =
173 BinaryBuffer<{ binary_buffer_length(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)) }>;
174pub fn new_buffer() -> Epd2In9Buffer {
176 Epd2In9Buffer::new(Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32))
177}
178
179const DRIVER_OUTPUT_INIT_DATA: [u8; 3] = [0x27, 0x01, 0x00];
187const BOOSTER_SOFT_START_INIT_DATA: [u8; 3] = [0xD7, 0xD6, 0x9D];
190trait StateInternal {}
195#[allow(private_bounds)]
196pub trait State: StateInternal {}
197pub trait StateAwake: State {}
198
199macro_rules! impl_base_state {
200 ($state:ident) => {
201 impl StateInternal for $state {}
202 impl State for $state {}
203 };
204}
205
206#[cfg_attr(feature = "defmt", derive(defmt::Format))]
207#[derive(Debug, Clone, Copy, PartialEq)]
208pub struct StateUninitialized();
209impl_base_state!(StateUninitialized);
210impl StateAwake for StateUninitialized {}
211
212#[cfg_attr(feature = "defmt", derive(defmt::Format))]
213#[derive(Debug, Clone, Copy, PartialEq)]
214pub struct StateReady {
215 mode: RefreshMode,
216}
217impl_base_state!(StateReady);
218impl StateAwake for StateReady {}
219
220#[cfg_attr(feature = "defmt", derive(defmt::Format))]
221#[derive(Debug, Clone, Copy, PartialEq)]
222pub struct StateAsleep<W: StateAwake> {
223 wake_state: W,
224}
225impl<W: StateAwake> StateInternal for StateAsleep<W> {}
226impl<W: StateAwake> State for StateAsleep<W> {}
227
228pub struct Epd2In9<HW, STATE> {
237 hw: HW,
238 state: STATE,
239}
240
241impl<HW> Epd2In9<HW, StateUninitialized>
242where
243 HW: DcHw + ResetHw + BusyHw + DelayHw + ErrorHw + SpiHw,
244 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
245 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
246 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
247 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
248{
249 pub fn new(hw: HW) -> Self {
250 Epd2In9 {
251 hw,
252 state: StateUninitialized(),
253 }
254 }
255}
256
257impl<HW, STATE> Epd2In9<HW, STATE>
258where
259 HW: DcHw + ResetHw + BusyHw + DelayHw + ErrorHw + SpiHw,
260 STATE: StateAwake,
261 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
262 + From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
263 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
264 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
265{
266 pub async fn init(
268 mut self,
269 spi: &mut HW::Spi,
270 mode: RefreshMode,
271 ) -> Result<Epd2In9<HW, StateReady>, HW::Error> {
272 debug!("Initialising display");
273 self = self.reset().await?;
274
275 self.send(spi, Command::SwReset, &[]).await?;
277
278 self.send(spi, Command::DriverOutputControl, &DRIVER_OUTPUT_INIT_DATA)
279 .await?;
280 self.send(
281 spi,
282 Command::BoosterSoftStartControl,
283 &BOOSTER_SOFT_START_INIT_DATA,
284 )
285 .await?;
286 self.send(spi, Command::DataEntryModeSetting, &[0b11])
288 .await?;
289
290 self.send(spi, Command::WriteVcom, &[0xA8]).await?;
293 self.send(spi, Command::SetDummyLinePeriod, &[0x1A]).await?;
295 self.send(spi, Command::SetGateLineWidth, &[0x08]).await?;
297
298 let mut epd = Epd2In9 {
299 hw: self.hw,
300 state: StateReady { mode },
301 };
302 epd.set_refresh_mode_impl(spi, mode).await?;
303 Ok(epd)
304 }
305}
306
307impl<HW, STATE> Epd2In9<HW, STATE>
308where
309 HW: DcHw + BusyHw + ErrorHw + SpiHw,
310 STATE: StateAwake,
311 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
312 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
313 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
314{
315 pub async fn set_border(
321 &mut self,
322 spi: &mut HW::Spi,
323 color: BinaryColor,
324 ) -> Result<(), HW::Error> {
325 let border_setting: u8 = match color {
326 BinaryColor::Off => 0x00,
327 BinaryColor::On => 0x01,
328 };
329 self.send(spi, Command::BorderWaveformControl, &[border_setting])
330 .await
331 }
332
333 pub async fn send(
335 &mut self,
336 spi: &mut HW::Spi,
337 command: Command,
338 data: &[u8],
339 ) -> Result<(), HW::Error> {
340 use crate::hw::CommandDataSend;
341 self.hw.send(spi, command.register(), data).await
342 }
343}
344
345impl<HW> Epd2In9<HW, StateReady>
346where
347 HW: DcHw + BusyHw + DelayHw + ErrorHw + SpiHw,
348 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
349 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
350 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
351{
352 pub async fn set_refresh_mode(
354 &mut self,
355 spi: &mut HW::Spi,
356 mode: RefreshMode,
357 ) -> Result<(), HW::Error> {
358 if self.state.mode == mode {
359 Ok(())
360 } else {
361 debug!("Changing refresh mode to {:?}", mode);
362 self.set_refresh_mode_impl(spi, mode).await?;
363 Ok(())
364 }
365 }
366
367 pub async fn set_window(
372 &mut self,
373 spi: &mut HW::Spi,
374 shape: Rectangle,
375 ) -> Result<(), HW::Error> {
376 let x_start = shape.top_left.x;
379 let x_end = x_start + shape.size.width as i32 - 1;
380 debug_assert!(
381 x_start % 8 == 0 && x_end % 8 == 7,
382 "window's top_left.x and width must be 8-bit aligned"
383 );
384 let x_start_byte = ((x_start >> 3) & 0xFF) as u8;
385 let x_end_byte = ((x_end >> 3) & 0xFF) as u8;
386 self.send(spi, Command::SetRamXStartEnd, &[x_start_byte, x_end_byte])
387 .await?;
388
389 let (y_start_low, y_start_high) = split_low_and_high(shape.top_left.y as u16);
390 let (y_end_low, y_end_high) =
391 split_low_and_high((shape.top_left.y + shape.size.height as i32 - 1) as u16);
392 self.send(
393 spi,
394 Command::SetRamYStartEnd,
395 &[y_start_low, y_start_high, y_end_low, y_end_high],
396 )
397 .await?;
398
399 Ok(())
400 }
401
402 pub async fn set_cursor(
407 &mut self,
408 spi: &mut HW::Spi,
409 position: Point,
410 ) -> Result<(), HW::Error> {
411 debug_assert_eq!(position.x % 8, 0, "position.x must be 8-bit aligned");
414
415 self.send(spi, Command::SetRamX, &[(position.x >> 3) as u8])
416 .await?;
417 let (y_low, y_high) = split_low_and_high(position.y as u16);
418 self.send(spi, Command::SetRamY, &[y_low, y_high]).await?;
419 Ok(())
420 }
421
422 async fn set_refresh_mode_impl(
423 &mut self,
424 spi: &mut HW::Spi,
425 mode: RefreshMode,
426 ) -> Result<(), HW::Error> {
427 self.send(spi, Command::WriteLut, mode.lut()).await?;
428 self.state.mode = mode;
429
430 match mode {
432 RefreshMode::Partial => {
433 self.send(spi, Command::DisplayUpdateControl1, &[0x00])
434 .await
435 }
436 RefreshMode::PartialBlackBypass => {
437 self.send(spi, Command::DisplayUpdateControl1, &[0x90])
438 .await
439 }
440 RefreshMode::PartialWhiteBypass => {
441 self.send(spi, Command::DisplayUpdateControl1, &[0x80])
442 .await
443 }
444 _ => Ok(()),
445 }
446 }
447}
448
449impl<HW> Displayable<HW::Spi, HW::Error> for Epd2In9<HW, StateReady>
450where
451 HW: DcHw + BusyHw + DelayHw + ErrorHw + SpiHw,
452 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
453 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
454 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
455{
456 async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error> {
457 debug!("Updating display");
468
469 self.send(spi, Command::DisplayUpdateControl2, &[0xC4])
470 .await?;
471 self.send(spi, Command::MasterActivation, &[]).await?;
472 self.send(spi, Command::Noop, &[]).await?;
473 Ok(())
474 }
475}
476
477impl<HW> DisplaySimple<1, 1, HW::Spi, HW::Error> for Epd2In9<HW, StateReady>
478where
479 HW: DcHw + BusyHw + DelayHw + ErrorHw + SpiHw,
480 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
481 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
482 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
483{
484 async fn display_framebuffer(
485 &mut self,
486 spi: &mut HW::Spi,
487 buf: &dyn BufferView<1, 1>,
488 ) -> Result<(), HW::Error> {
489 self.write_framebuffer(spi, buf).await?;
490 self.update_display(spi).await
491 }
492
493 async fn write_framebuffer(
494 &mut self,
495 spi: &mut HW::Spi,
496 buf: &dyn BufferView<1, 1>,
497 ) -> Result<(), HW::Error> {
498 let buffer_bounds = buf.window();
499 self.set_window(spi, buffer_bounds).await?;
500 self.set_cursor(spi, buffer_bounds.top_left).await?;
501 self.send(spi, Command::WriteRam, buf.data()[0]).await
502 }
503}
504
505impl<HW> DisplayPartial<1, 1, HW::Spi, HW::Error> for Epd2In9<HW, StateReady>
506where
507 HW: DcHw + BusyHw + DelayHw + ErrorHw + SpiHw,
508 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
509 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
510 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
511{
512 async fn write_base_framebuffer(
518 &mut self,
519 spi: &mut HW::Spi,
520 buf: &dyn BufferView<1, 1>,
521 ) -> Result<(), HW::Error> {
522 let buffer_bounds = buf.window();
523 self.set_window(spi, buffer_bounds).await?;
524 self.set_cursor(spi, buffer_bounds.top_left).await?;
525 self.send(spi, Command::WriteOldRam, buf.data()[0]).await
526 }
527}
528
529async fn reset_impl<HW>(hw: &mut HW) -> Result<(), HW::Error>
530where
531 HW: ResetHw + DelayHw + ErrorHw,
532 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
533{
534 debug!("Resetting EPD");
535 hw.reset().set_low()?;
537 hw.delay().delay_ms(10).await;
538 hw.reset().set_high()?;
539 hw.delay().delay_ms(10).await;
540 Ok(())
541}
542
543impl<HW, STATE> Reset<HW::Error> for Epd2In9<HW, STATE>
544where
545 HW: ResetHw + DelayHw + ErrorHw,
546 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
547 STATE: StateAwake,
548{
549 type DisplayOut = Epd2In9<HW, STATE>;
550
551 async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
552 reset_impl(&mut self.hw).await?;
553 Ok(self)
554 }
555}
556
557impl<HW, W> Reset<HW::Error> for Epd2In9<HW, StateAsleep<W>>
558where
559 HW: ResetHw + DelayHw + ErrorHw,
560 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>,
561 W: StateAwake,
562{
563 type DisplayOut = Epd2In9<HW, W>;
564
565 async fn reset(mut self) -> Result<Self::DisplayOut, HW::Error> {
566 reset_impl(&mut self.hw).await?;
567 Ok(Epd2In9 {
568 hw: self.hw,
569 state: self.state.wake_state,
570 })
571 }
572}
573
574impl<HW, STATE> Sleep<HW::Spi, HW::Error> for Epd2In9<HW, STATE>
575where
576 HW: DcHw + BusyHw + ErrorHw + SpiHw,
577 HW::Error: From<<HW::Dc as embedded_hal::digital::ErrorType>::Error>
578 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
579 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
580 STATE: StateAwake,
581{
582 type DisplayOut = Epd2In9<HW, StateAsleep<STATE>>;
583
584 async fn sleep(mut self, spi: &mut HW::Spi) -> Result<Self::DisplayOut, HW::Error>
585where {
586 debug!("Sleeping EPD");
587 self.send(spi, Command::DeepSleepMode, &[0x01]).await?;
588 Ok(Epd2In9 {
589 hw: self.hw,
590 state: StateAsleep {
591 wake_state: self.state,
592 },
593 })
594 }
595}
596
597impl<HW, W> Wake<HW::Spi, HW::Error> for Epd2In9<HW, StateAsleep<W>>
598where
599 HW: ResetHw + BusyHw + DelayHw + ErrorHw + SpiHw,
600 HW::Error: From<<HW::Reset as embedded_hal::digital::ErrorType>::Error>
601 + From<<HW::Busy as embedded_hal::digital::ErrorType>::Error>
602 + From<<HW::Spi as embedded_hal_async::spi::ErrorType>::Error>,
603 W: StateAwake,
604{
605 type DisplayOut = Epd2In9<HW, W>;
606
607 async fn wake(self, _spi: &mut HW::Spi) -> Result<Self::DisplayOut, HW::Error> {
608 debug!("Waking EPD");
609 self.reset().await
610 }
612}