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}