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}