Skip to main content

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;
43pub mod epd7in5_v2;
44/// This module provides hardware abstraction traits that can be used by display drivers.
45/// You should implement all the traits on a single struct, so that you can pass this one
46/// hardware struct to your display driver.
47///
48/// Example that remains generic over the specific SPI bus:
49///
50/// ```
51/// # use core::convert::Infallible;
52/// # use core::marker::PhantomData;
53/// use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice as EmbassySpiDevice;
54/// use embassy_embedded_hal::shared_bus::SpiDeviceError;
55/// use embassy_rp::gpio::{Input, Level, Output, Pin, Pull};
56/// use embassy_rp::spi;
57/// use embassy_rp::Peri;
58/// use embassy_sync::blocking_mutex::raw::NoopRawMutex;
59/// use embedded_hal::digital::PinState;
60/// use epd_waveshare_async::hw::{BusyHw, DcHw, DelayHw, ErrorHw, ResetHw, SpiHw};
61/// use thiserror::Error as ThisError;
62///
63/// /// Defines the hardware to use for connecting to the display.
64/// pub struct DisplayHw<'a, SPI> {
65///     dc: Output<'a>,
66///     reset: Output<'a>,
67///     busy: Input<'a>,
68///     delay: embassy_time::Delay,
69///     _spi_type: PhantomData<SPI>,
70/// }
71///
72/// impl<'a, SPI: spi::Instance> DisplayHw<'a, SPI> {
73///     pub fn new<DC: Pin, RESET: Pin, BUSY: Pin>(
74///         dc: Peri<'a, DC>,
75///         reset: Peri<'a, RESET>,
76///         busy: Peri<'a, BUSY>,
77///     ) -> Self {
78///         let dc = Output::new(dc, Level::High);
79///         let reset = Output::new(reset, Level::High);
80///         let busy = Input::new(busy, Pull::Up);
81///
82///         Self {
83///             dc,
84///             reset,
85///             busy,
86///             delay: embassy_time::Delay,
87///             _spi_type: PhantomData,
88///         }
89///     }
90/// }
91///
92/// impl<'a, SPI> ErrorHw for DisplayHw<'a, SPI> {
93///     type Error = Error;
94/// }
95///
96/// impl<'a, SPI> DcHw for DisplayHw<'a, SPI> {
97///     type Dc = Output<'a>;
98///
99///     fn dc(&mut self) -> &mut Self::Dc {
100///         &mut self.dc
101///     }
102/// }
103///
104/// impl<'a, SPI> ResetHw for DisplayHw<'a, SPI> {
105///     type Reset = Output<'a>;
106///
107///     fn reset(&mut self) -> &mut Self::Reset {
108///         &mut self.reset
109///     }
110/// }
111///
112/// impl<'a, SPI> BusyHw for DisplayHw<'a, SPI> {
113///     type Busy = Input<'a>;
114///
115///     fn busy(&mut self) -> &mut Self::Busy {
116///         &mut self.busy
117///     }
118///
119///     fn busy_when(&self) -> embedded_hal::digital::PinState {
120///         epd_waveshare_async::epd2in9::DEFAULT_BUSY_WHEN
121///     }
122/// }
123///
124/// impl<'a, SPI> DelayHw for DisplayHw<'a, SPI> {
125///     type Delay = embassy_time::Delay;
126///
127///     fn delay(&mut self) -> &mut Self::Delay {
128///         &mut self.delay
129///     }
130/// }
131///
132/// impl<'a, SPI: spi::Instance + 'a> SpiHw for DisplayHw<'a, SPI> {
133///     type Spi = EmbassySpiDevice<'a, NoopRawMutex, spi::Spi<'a, SPI, spi::Async>, Output<'a>>;
134/// }
135///
136/// type RawSpiError = SpiDeviceError<spi::Error, Infallible>;
137///
138/// #[derive(Debug, ThisError)]
139/// pub enum Error {
140///     #[error("SPI error: {0:?}")]
141///     SpiError(RawSpiError),
142/// }
143///
144/// impl From<Infallible> for Error {
145///     fn from(_: Infallible) -> Self {
146///         unreachable!()
147///     }
148/// }
149///
150/// impl From<RawSpiError> for Error {
151///     fn from(e: RawSpiError) -> Self {
152///         Error::SpiError(e)
153///     }
154/// }
155/// ```
156pub mod hw;
157
158mod log;
159
160use crate::buffer::BufferView;
161
162/// Displays that have a hardware reset.
163pub trait Reset<ERROR> {
164    type DisplayOut;
165
166    /// Hardware resets the display.
167    async fn reset(self) -> Result<Self::DisplayOut, ERROR>;
168}
169
170/// Displays that can sleep to save power.
171pub trait Sleep<SPI: SpiDevice, ERROR> {
172    type DisplayOut;
173
174    /// Puts the display to sleep.
175    async fn sleep(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
176}
177
178/// Displays that can be woken from a sleep state.
179pub trait Wake<SPI: SpiDevice, ERROR> {
180    type DisplayOut;
181
182    /// Wakes and re-initialises the display (if necessary) if it's asleep.
183    async fn wake(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
184}
185
186/// Displays that can be cleared with their base color.
187pub trait Clear<SPI: SpiDevice, ERROR> {
188    /// Fills the display with a specific color.
189    async fn clear(&mut self, spi: &mut SPI) -> Result<(), ERROR>;
190}
191
192/// Base trait for any display where the display can be updated separate from its framebuffer data.
193pub trait Displayable<SPI: SpiDevice, ERROR> {
194    /// Updates (refreshes) the display based on what has been written to the framebuffer.
195    async fn update_display(&mut self, spi: &mut SPI) -> Result<(), ERROR>;
196}
197
198/// Simple displays that support writing and displaying framebuffers of a certain bit configuration.
199///
200/// `BITS` indicates the colour depth of each frame, and `FRAMES` indicates the total number of frames that
201/// represent a complete image. For example, some 4-colour greyscale display might accept data as two
202/// separate 1-bit frames instead of one frame of 2-bit pixels. This distinction is exposed so that
203/// framebuffers can be written directly to displays without temp copies or transformations.
204pub trait DisplaySimple<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
205    Displayable<SPI, ERROR>
206{
207    /// Writes the given buffer's data into the main framebuffer to be displayed on the next call to [Displayable::update_display].
208    async fn write_framebuffer(
209        &mut self,
210        spi: &mut SPI,
211        buf: &dyn BufferView<BITS, FRAMES>,
212    ) -> Result<(), ERROR>;
213
214    /// A shortcut for calling [DisplaySimple::write_framebuffer] followed by [Displayable::update_display].
215    async fn display_framebuffer(
216        &mut self,
217        spi: &mut SPI,
218        buf: &dyn BufferView<BITS, FRAMES>,
219    ) -> Result<(), ERROR>;
220}
221
222/// Displays that support a partial update, where a "diff" framebuffer is diffed against a base
223/// framebuffer, and only the changed pixels from the diff are actually updated.
224pub trait DisplayPartial<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
225    DisplaySimple<BITS, FRAMES, SPI, ERROR>
226{
227    /// Writes the buffer to the base framebuffer that the main framebuffer layer (written with
228    /// [DisplaySimple::write_framebuffer]) will be diffed against.
229    /// Only pixels that differ will be updated.
230    ///
231    /// For standard use, you probably only need to call this once before the first partial display,
232    /// as the main framebuffer becomes the diff base after a call to [Displayable::update_display].
233    async fn write_base_framebuffer(
234        &mut self,
235        spi: &mut SPI,
236        buf: &dyn BufferView<BITS, FRAMES>,
237    ) -> Result<(), ERROR>;
238}