epd_waveshare_async/
lib.rs

1//! This crate provides an `async`/`await` interface for controlling Waveshare E-Paper displays.
2//!
3//! It is built on top of `embedded-hal-async` and `embedded-graphics`, making it compatible with a
4//! wide range of embedded platforms.
5//!
6//! ## Core traits
7//!
8//! The crate is organized around two main traits:
9//!
10//! - [`Epd`]: This trait defines the core functionality for interacting with an E-Paper display,
11//!   such as initialization, refreshing, writing image data, and managing sleep states.
12//!   Implementations of this trait (e.g., [`epd2in9::Epd2In9`]) provide concrete display-specific
13//!   logic. Concrete implementations may also provide further functionality that doesn't fit in
14//!   the general `Epd` trait (e.g. modifying the border on the Epd2In9 screen).
15//!
16//! - [`EpdHw`]: This trait abstracts over the underlying hardware components required to control an
17//!   E-Paper display, including SPI communication, GPIO pins (for Data/Command, Reset, and Busy
18//!   signals), and a delay timer. You need to implement this trait for your chosen peripherals.
19//!   This trades off some set up code (implementing this trait), for simple type signatures with
20//!   only one generic parameter.
21//!
22//! Additionally, the crate provides:
23//!
24//! - `buffer` module: Contains utilities for creating and managing efficient display buffers that
25//!   implement `embedded-graphics::DrawTarget`. These are designed to be fast and compact.
26//! - `<display>` modules: each display lives in its own module, such as `epd2in9` for the 2.9"
27//!   e-paper display.
28#![no_std]
29
30use core::error::Error as CoreError;
31
32use embedded_graphics::{
33    prelude::{DrawTarget, Point},
34    primitives::Rectangle,
35};
36use embedded_hal::digital::{ErrorType as PinErrorType, InputPin, OutputPin};
37use embedded_hal_async::{
38    delay::DelayNs,
39    digital::Wait,
40    spi::{ErrorType as SpiErrorType, SpiDevice},
41};
42
43pub mod buffer;
44pub mod epd2in9;
45
46mod log;
47
48#[allow(async_fn_in_trait)]
49pub trait Epd<HW>
50where
51    HW: EpdHw,
52{
53    type RefreshMode;
54    type Buffer: DrawTarget;
55
56    /// Creates a buffer for use with this display.
57    fn new_buffer(&self) -> Self::Buffer;
58
59    fn width(&self) -> u32;
60
61    fn height(&self) -> u32;
62
63    /// Initialise the display. This must be called before any other operations.
64    async fn init(&mut self, spi: &mut HW::Spi, mode: Self::RefreshMode) -> Result<(), HW::Error>;
65
66    /// Sets the refresh mode for the display.
67    async fn set_refresh_mode(
68        &mut self,
69        spi: &mut HW::Spi,
70        mode: Self::RefreshMode,
71    ) -> Result<(), HW::Error>;
72
73    /// Hardware reset the display.
74    async fn reset(&mut self) -> Result<(), HW::Error>;
75
76    /// Puts the display to sleep.
77    async fn sleep(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error>;
78
79    /// Wakes and re-initialises the display (if necessary) if it's asleep.
80    async fn wake(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error>;
81
82    /// Writes the buffer's data to the display and displays it.
83    async fn display_buffer(
84        &mut self,
85        spi: &mut HW::Spi,
86        buffer: &Self::Buffer,
87    ) -> Result<(), HW::Error>;
88
89    /// Writes the buffer's data to the display's internal framebuffer, but does not display it.
90    async fn write_framebuffer(
91        &mut self,
92        spi: &mut HW::Spi,
93        buffer: &Self::Buffer,
94    ) -> Result<(), HW::Error>;
95
96    /// Sets the window to write to during a call to [Epd::write_image]. This can enable partial
97    /// writes to a subsection of the display.
98    async fn set_window(&mut self, spi: &mut HW::Spi, shape: Rectangle) -> Result<(), HW::Error>;
99
100    /// Sets the cursor position for where the next byte of image data will be written.
101    async fn set_cursor(
102        &mut self,
103        spi: &mut HW::Spi,
104        position: Point,
105    ) -> Result<(), <HW as EpdHw>::Error>;
106
107    /// Writes raw image data, starting at the current cursor position and auto-incrementing x and
108    /// y within the current window. By default, x should increment first, then y (data is written
109    /// in rows).
110    async fn write_image(&mut self, spi: &mut HW::Spi, image: &[u8]) -> Result<(), HW::Error>;
111
112    /// Updates (refreshes) the display based on what has been written to RAM. Note that this can be
113    /// stateful. For example, on the Epd2in9 display, there are two RAM buffers. Calling this
114    /// function swaps the active buffer. Consider this scenario:
115    ///
116    /// 1. [Epd::write_image] is used to turn the RAM all white.
117    /// 2. [Epd::update_display] is called, which refreshes the display to be all white.
118    /// 3. [Epd::write_image] is used to turn the RAM all black.
119    /// 4. [Epd::update_display] is called, which refreshes the display to be all black.
120    /// 5. [Epd::update_display] is called again, which refreshes the display to be all white again.
121    async fn update_display(&mut self, spi: &mut HW::Spi) -> Result<(), HW::Error>;
122}
123
124/// Provides access to the hardware needed to control an EPD.
125///
126/// This greatly simplifies the generics needed by the `Epd` trait and implementing types at the cost of implementing this trait.
127///
128/// In this example, we make the EPD generic over just the SPI type, but can drop generics for the pins and delay type.
129///
130/// ```rust
131/// use core::convert::Infallible;
132///
133/// use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
134/// use embassy_embedded_hal::shared_bus::SpiDeviceError;
135/// use embassy_rp::gpio::{Input, Output};
136/// use embassy_rp::spi::{self, Spi};
137/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
138/// use embassy_time::Delay;
139/// use epd_waveshare_async::EpdHw;
140/// use thiserror::Error as ThisError;
141///
142/// /// Define an error type that can convert from the SPI and GPIO errors.
143/// #[derive(Debug, ThisError)]
144/// enum Error {
145///   #[error("SPI error: {0:?}")]
146///   SpiError(SpiDeviceError<spi::Error, Infallible>),
147/// }
148///
149/// impl From<Infallible> for Error {
150///     fn from(_: Infallible) -> Self {
151///         // GPIO errors are infallible, i.e. they can't occur, so this should be unreachable.
152///         unreachable!()
153///     }
154/// }
155///
156/// impl From<SpiDeviceError<spi::Error, Infallible>> for Error {
157///     fn from(e: SpiDeviceError<spi::Error, Infallible>) -> Self {
158///         Error::SpiError(e)
159///     }
160/// }
161///
162/// struct RpEpdHw<'a, SPI: spi::Instance + 'a> {
163///     dc: Output<'a>,
164///     reset: Output<'a>,
165///     busy: Input<'a>,
166///     delay: Delay,
167///     _phantom: core::marker::PhantomData<SPI>,
168/// }
169///
170/// impl <'a, SPI: spi::Instance + 'a> EpdHw for RpEpdHw<'a, SPI> {
171///     type Spi = SpiDevice<'a, CriticalSectionRawMutex, Spi<'a, SPI, spi::Async>, Output<'a>>;
172///     type Dc = Output<'a>;
173///     type Reset = Output<'a>;
174///     type Busy = Input<'a>;
175///     type Delay = Delay;
176///     type Error = Error;
177///
178///     fn dc(&mut self) -> &mut Self::Dc {
179///       &mut self.dc
180///     }
181///
182///     fn reset(&mut self) -> &mut Self::Reset {
183///       &mut self.reset
184///     }
185///
186///     fn busy(&mut self) -> &mut Self::Busy {
187///       &mut self.busy
188///     }
189///
190///     fn delay(&mut self) -> &mut Self::Delay {
191///       &mut self.delay
192///     }
193/// }
194/// ```
195pub trait EpdHw {
196    type Spi: SpiDevice;
197    type Dc: OutputPin;
198    type Reset: OutputPin;
199    type Busy: InputPin + Wait;
200    type Delay: DelayNs;
201    type Error: CoreError
202        + From<<Self::Spi as SpiErrorType>::Error>
203        + From<<Self::Dc as PinErrorType>::Error>
204        + From<<Self::Reset as PinErrorType>::Error>
205        + From<<Self::Busy as PinErrorType>::Error>;
206
207    fn dc(&mut self) -> &mut Self::Dc;
208    fn reset(&mut self) -> &mut Self::Reset;
209    fn busy(&mut self) -> &mut Self::Busy;
210    fn delay(&mut self) -> &mut Self::Delay;
211}