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//! ### Hardware
9//!
10//! The user must implement the `XHw` traits for their hardware that are needed by their display.
11//! These traits abstract over common hardware functionality that displays need, like SPI
12//! communication, GPIO pins (for Data/Command, Reset, and Busy) and a delay timer. You need to
13//! implement these traits for your chosen peripherals. This trades off some set up code (
14//! implementing these traits), for simple type signatures with fewer generic parameters.
15//!
16//! See the [crate::hw] module for more.
17//!
18//! ### Functionality
19//!
20//! Functionality is split into composable traits, to enable granular support per display, and
21//! stateful functionality that can be checked at compilation time.
22//!
23//! * [Reset]: basic hardware reset support
24//! * [Sleep]: displays that can be put to sleep
25//! * [Wake]: displays that can be woken from sleep
26//! * [DisplaySimple]: basic support for writing and displaying a single framebuffer
27//! * [DisplayPartial]: support for partial refresh using a diff
28//!
29//! Additionally, the crate provides:
30//!
31//! - [`buffer`] module: Contains utilities for creating and managing efficient display buffers that
32//!   implement `embedded-graphics::DrawTarget`. These are designed to be fast and compact.
33//! - various `<display>` modules: each display lives in its own module, such as `epd2in9` for the 2.9"
34//!   e-paper display.
35#![no_std]
36#![allow(async_fn_in_trait)]
37
38use embedded_hal_async::spi::SpiDevice;
39
40pub mod buffer;
41pub mod epd2in9;
42pub mod epd2in9_v2;
43/// This module provides hardware abstraction traits that can be used by display drivers.
44/// You should implement all the traits on a single struct, so that you can pass this one
45/// hardware struct to your display driver.
46///
47/// Example that remains generic over the specific SPI bus:
48///
49/// ```
50/// # use core::convert::Infallible;
51/// # use core::marker::PhantomData;
52/// use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice as EmbassySpiDevice;
53/// use embassy_embedded_hal::shared_bus::SpiDeviceError;
54/// use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
55/// use embassy_rp::spi;
56/// use embassy_rp::Peri;
57/// use embassy_sync::blocking_mutex::raw::NoopRawMutex;
58/// use embedded_hal::digital::PinState;
59/// use epd_waveshare_async::hw::{BusyHw, DcHw, DelayHw, ErrorHw, ResetHw, SpiHw};
60/// use thiserror::Error as ThisError;
61///
62/// /// Defines the hardware to use for connecting to the display.
63/// pub struct DisplayHw<'a, SPI> {
64///     dc: Output<'a>,
65///     reset: Output<'a>,
66///     busy: Input<'a>,
67///     delay: embassy_time::Delay,
68///     _spi_type: PhantomData<SPI>,
69/// }
70///
71/// impl<'a, SPI: spi::Instance> DisplayHw<'a, SPI> {
72///     pub fn new<DC: Pin, RESET: Pin, BUSY: Pin>(
73///         dc: Peri<'a, DC>,
74///         reset: Peri<'a, RESET>,
75///         busy: Peri<'a, BUSY>,
76///     ) -> Self {
77///         let dc = Output::new(dc, Level::High);
78///         let reset = Output::new(reset, Level::High);
79///         let busy = Input::new(busy, Pull::Up);
80///
81///         Self {
82///             dc,
83///             reset,
84///             busy,
85///             delay: embassy_time::Delay,
86///             _spi_type: PhantomData,
87///         }
88///     }
89/// }
90///
91/// impl<'a, SPI> ErrorHw for DisplayHw<'a, SPI> {
92///     type Error = Error;
93/// }
94///
95/// impl<'a, SPI> DcHw for DisplayHw<'a, SPI> {
96///     type Dc = Output<'a>;
97///
98///     fn dc(&mut self) -> &mut Self::Dc {
99///         &mut self.dc
100///     }
101/// }
102///
103/// impl<'a, SPI> ResetHw for DisplayHw<'a, SPI> {
104///     type Reset = Output<'a>;
105///
106///     fn reset(&mut self) -> &mut Self::Reset {
107///         &mut self.reset
108///     }
109/// }
110///
111/// impl<'a, SPI> BusyHw for DisplayHw<'a, SPI> {
112///     type Busy = Input<'a>;
113///
114///     fn busy(&mut self) -> &mut Self::Busy {
115///         &mut self.busy
116///     }
117///
118///     fn busy_when(&self) -> embedded_hal::digital::PinState {
119///         epd_waveshare_async::epd2in9::DEFAULT_BUSY_WHEN
120///     }
121/// }
122///
123/// impl<'a, SPI> DelayHw for DisplayHw<'a, SPI> {
124///     type Delay = embassy_time::Delay;
125///
126///     fn delay(&mut self) -> &mut Self::Delay {
127///         &mut self.delay
128///     }
129/// }
130///
131/// impl<'a, SPI: spi::Instance + 'a> SpiHw for DisplayHw<'a, SPI> {
132///     type Spi = EmbassySpiDevice<'a, NoopRawMutex, spi::Spi<'a, SPI, spi::Async>, Output<'a>>;
133/// }
134///
135/// type RawSpiError = SpiDeviceError<spi::Error, Infallible>;
136///
137/// #[derive(Debug, ThisError)]
138/// pub enum Error {
139///     #[error("SPI error: {0:?}")]
140///     SpiError(RawSpiError),
141/// }
142///
143/// impl From<Infallible> for Error {
144///     fn from(_: Infallible) -> Self {
145///         unreachable!()
146///     }
147/// }
148///
149/// impl From<RawSpiError> for Error {
150///     fn from(e: RawSpiError) -> Self {
151///         Error::SpiError(e)
152///     }
153/// }
154/// ```
155pub mod hw;
156
157mod log;
158
159use crate::buffer::BufferView;
160
161/// Displays that have a hardware reset.
162pub trait Reset<ERROR> {
163    type DisplayOut;
164
165    /// Hardware resets the display.
166    async fn reset(self) -> Result<Self::DisplayOut, ERROR>;
167}
168
169/// Displays that can sleep to save power.
170pub trait Sleep<SPI: SpiDevice, ERROR> {
171    type DisplayOut;
172
173    /// Puts the display to sleep.
174    async fn sleep(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
175}
176
177/// Displays that can be woken from a sleep state.
178pub trait Wake<SPI: SpiDevice, ERROR> {
179    type DisplayOut;
180
181    /// Wakes and re-initialises the display (if necessary) if it's asleep.
182    async fn wake(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
183}
184
185/// Base trait for any display where the display can be updated separate from its framebuffer data.
186pub trait Displayable<SPI: SpiDevice, ERROR> {
187    /// Updates (refreshes) the display based on what has been written to the framebuffer.
188    async fn update_display(&mut self, spi: &mut SPI) -> Result<(), ERROR>;
189}
190
191/// Simple displays that support writing and displaying framebuffers of a certain bit configuration.
192///
193/// `BITS` indicates the colour depth of each frame, and `FRAMES` indicates the total number of frames that
194/// represent a complete image. For example, some 4-colour greyscale display might accept data as two
195/// separate 1-bit frames instead of one frame of 2-bit pixels. This distinction is exposed so that
196/// framebuffers can be written directly to displays without temp copies or transformations.
197pub trait DisplaySimple<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
198    Displayable<SPI, ERROR>
199{
200    /// Writes the given buffer's data into the main framebuffer to be displayed on the next call to [Displayable::update_display].
201    async fn write_framebuffer(
202        &mut self,
203        spi: &mut SPI,
204        buf: &dyn BufferView<BITS, FRAMES>,
205    ) -> Result<(), ERROR>;
206
207    /// A shortcut for calling [DisplaySimple::write_framebuffer] followed by [Displayable::update_display].
208    async fn display_framebuffer(
209        &mut self,
210        spi: &mut SPI,
211        buf: &dyn BufferView<BITS, FRAMES>,
212    ) -> Result<(), ERROR>;
213}
214
215/// Displays that support a partial update, where a "diff" framebuffer is diffed against a base
216/// framebuffer, and only the changed pixels from the diff are actually updated.
217pub trait DisplayPartial<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
218    DisplaySimple<BITS, FRAMES, SPI, ERROR>
219{
220    /// Writes the buffer to the base framebuffer that the main framebuffer layer (written with
221    /// [DisplaySimple::write_framebuffer]) will be diffed against.
222    /// Only pixels that differ will be updated.
223    ///
224    /// For standard use, you probably only need to call this once before the first partial display,
225    /// as the main framebuffer becomes the diff base after a call to [Displayable::update_display].
226    async fn write_base_framebuffer(
227        &mut self,
228        spi: &mut SPI,
229        buf: &dyn BufferView<BITS, FRAMES>,
230    ) -> Result<(), ERROR>;
231}