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 use must implement the [`EpdHw`] trait for their hardware. This trait abstracts over the
11//! underlying hardware components required to control an E-Paper display, including SPI
12//! communication, GPIO pins (for Data/Command, Reset, and Busy and a delay timer. You need to
13//! implement this trait for your chosen peripherals. This trades off some set up code (implementing
14//! this trait), for simple type signatures with fewer generic parameters.
15//!
16//! ### Functionality
17//!
18//! Functionality is split into composable traits, to enable granular support per display, and
19//! stateful functionality that can be checked at compilation time.
20//!
21//! * [Reset]: basic hardware reset support
22//! * [Sleep]: displays that can be put to sleep
23//! * [Wake]: displays that can be woken from sleep
24//! * [DisplaySimple]: basic support for writing and displaying a single framebuffer
25//! * [DisplayPartial]: support for partial refresh using a diff
26//!
27//! Additionally, the crate provides:
28//!
29//! - [`buffer`] module: Contains utilities for creating and managing efficient display buffers that
30//!   implement `embedded-graphics::DrawTarget`. These are designed to be fast and compact.
31//! - various `<display>` modules: each display lives in its own module, such as `epd2in9` for the 2.9"
32//!   e-paper display.
33#![no_std]
34#![allow(async_fn_in_trait)]
35
36use embedded_hal_async::spi::SpiDevice;
37
38pub mod buffer;
39pub mod epd2in9;
40pub mod epd2in9_v2;
41
42mod hw;
43mod log;
44
45use crate::buffer::BufferView;
46pub use crate::hw::EpdHw;
47
48/// Indicates usage errors due to incorrect states.
49///
50/// These errors are allowed to occur as runtime errors instead of being prevented at compile time
51/// through stateful types. The alternative was tried, but was awkward to use in practice since
52/// async traits are not dyn compatible.
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54#[derive(Debug, Clone)]
55pub enum Error {
56    Uninitialized,
57    Sleeping,
58    WrongRefreshMode,
59}
60
61/// Displays that have a hardware reset.
62pub trait Reset<ERROR> {
63    type DisplayOut;
64
65    /// Hardware resets the display.
66    async fn reset(self) -> Result<Self::DisplayOut, ERROR>;
67}
68
69/// Displays that can sleep to save power.
70pub trait Sleep<SPI: SpiDevice, ERROR> {
71    type DisplayOut;
72
73    /// Puts the display to sleep.
74    async fn sleep(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
75}
76
77/// Displays that can be woken from a sleep state.
78pub trait Wake<SPI: SpiDevice, ERROR> {
79    type DisplayOut;
80
81    /// Wakes and re-initialises the display (if necessary) if it's asleep.
82    async fn wake(self, spi: &mut SPI) -> Result<Self::DisplayOut, ERROR>;
83}
84
85/// Base trait for any display where the display can be updated separate from its framebuffer data.
86pub trait Displayable<SPI: SpiDevice, ERROR> {
87    /// Updates (refreshes) the display based on what has been written to RAM. Note that this can be
88    /// stateful, for example displays in [DisplayPartial] mode often swap two underlying
89    /// framebuffers on calls to this method, resulting in the following behaviour:
90    ///
91    /// 1. [DisplayPartial::write_diff_framebuffer] is used to turn the RAM all white.
92    /// 2. [Displayable::update_display] is called, which refreshes the display to be all white.
93    /// 3. [DisplayPartial::write_diff_framebuffer] is used to turn the RAM all black.
94    /// 4. [Displayable::update_display] is called, which refreshes the display to be all black.
95    /// 5. [Displayable::update_display] is called again, which refreshes the display to be all white again.
96    async fn update_display(&mut self, spi: &mut SPI) -> Result<(), ERROR>;
97}
98
99/// Simple displays that support writing and displaying framebuffers of a certain bit configuration.
100///
101/// `BITS` indicates the colour depth of each frame, and `FRAMES` indicates the total number of frames that
102/// represent a complete image. For example, some 4-colour greyscale displays accept data as two
103/// separate 1-bit frames instead of one frame of 2-bit pixels. This distinction is exposed so that
104/// framebuffers can be written directly to displays without temp copies or transformations.
105pub trait DisplaySimple<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
106    Displayable<SPI, ERROR>
107{
108    /// Writes the given buffer's data into the main framebuffer to be displayed on the next call to [Displayable::update_display].
109    async fn write_framebuffer(
110        &mut self,
111        spi: &mut SPI,
112        buf: &dyn BufferView<BITS, FRAMES>,
113    ) -> Result<(), ERROR>;
114
115    /// A shortcut for calling [DisplaySimple::write_framebuffer] followed by [Displayable::update_display].
116    async fn display_framebuffer(
117        &mut self,
118        spi: &mut SPI,
119        buf: &dyn BufferView<BITS, FRAMES>,
120    ) -> Result<(), ERROR>;
121}
122
123/// Displays that support a partial update, where a "diff" framebuffer is diffed against a base
124/// framebuffer, and only the changed pixels from the diff are actually updated.
125pub trait DisplayPartial<const BITS: usize, const FRAMES: usize, SPI: SpiDevice, ERROR>:
126    DisplaySimple<BITS, FRAMES, SPI, ERROR>
127{
128    /// Writes the buffer to the base framebuffer that the main framebuffer layer (written with
129    /// [DisplaySimple::write_framebuffer]) will be diffed against.
130    /// Only pixels that differ will be updated.
131    ///
132    /// For standard use, you probably only need to call this once before the first partial display,
133    /// as the main framebuffer becomes the diff base after a call to [Displayable::update_display].
134    async fn write_base_framebuffer(
135        &mut self,
136        spi: &mut SPI,
137        buf: &dyn BufferView<BITS, FRAMES>,
138    ) -> Result<(), ERROR>;
139}