epd_waveshare_async/
hw.rs

1use core::error::Error as CoreError;
2
3use embedded_hal::{
4    digital::{ErrorType as PinErrorType, InputPin, OutputPin},
5    spi::ErrorType as SpiErrorType,
6};
7use embedded_hal_async::{delay::DelayNs, digital::Wait, spi::SpiDevice};
8
9use crate::log::trace;
10
11/// Provides access to the hardware needed to control an EPD.
12///
13/// This greatly simplifies the generics needed by the `Epd` trait and implementing types at the cost of implementing this trait.
14///
15/// In this example, we make the EPD generic over just the SPI type, but can drop generics for the pins and delay type.
16///
17/// ```rust
18/// use core::convert::Infallible;
19///
20/// use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
21/// use embassy_embedded_hal::shared_bus::SpiDeviceError;
22/// use embassy_rp::gpio::{Input, Output};
23/// use embassy_rp::spi::{self, Spi};
24/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
25/// use embassy_time::Delay;
26/// use epd_waveshare_async::{EpdHw, Error as EpdError};
27/// use thiserror::Error as ThisError;
28///
29/// /// Define an error type that can convert from the SPI and GPIO errors.
30/// #[derive(Debug, ThisError)]
31/// enum Error {
32///   #[error("SPI error: {0:?}")]
33///   SpiError(SpiDeviceError<spi::Error, Infallible>),
34///   #[error("Display error: {0:?}")]
35///   DisplayError(EpdError),
36/// }
37///
38/// impl From<Infallible> for Error {
39///     fn from(_: Infallible) -> Self {
40///         // GPIO errors are infallible, i.e. they can't occur, so this should be unreachable.
41///         unreachable!()
42///     }
43/// }
44///
45/// impl From<SpiDeviceError<spi::Error, Infallible>> for Error {
46///     fn from(e: SpiDeviceError<spi::Error, Infallible>) -> Self {
47///         Error::SpiError(e)
48///     }
49/// }
50///
51/// impl From<EpdError> for Error {
52///     fn from(e: EpdError) -> Self {
53///         Error::DisplayError(e)
54///     }
55/// }
56///
57/// struct RpEpdHw<'a, SPI: spi::Instance + 'a> {
58///     dc: Output<'a>,
59///     reset: Output<'a>,
60///     busy: Input<'a>,
61///     delay: Delay,
62///     _phantom: core::marker::PhantomData<SPI>,
63/// }
64///
65/// impl <'a, SPI: spi::Instance + 'a> EpdHw for RpEpdHw<'a, SPI> {
66///     type Spi = SpiDevice<'a, CriticalSectionRawMutex, Spi<'a, SPI, spi::Async>, Output<'a>>;
67///     type Dc = Output<'a>;
68///     type Reset = Output<'a>;
69///     type Busy = Input<'a>;
70///     type Delay = Delay;
71///     type Error = Error;
72///
73///     fn dc(&mut self) -> &mut Self::Dc {
74///       &mut self.dc
75///     }
76///
77///     fn reset(&mut self) -> &mut Self::Reset {
78///       &mut self.reset
79///     }
80///
81///     fn busy(&mut self) -> &mut Self::Busy {
82///       &mut self.busy
83///     }
84///
85///     fn delay(&mut self) -> &mut Self::Delay {
86///       &mut self.delay
87///     }
88/// }
89/// ```
90pub trait EpdHw {
91    type Spi: SpiDevice;
92    type Dc: OutputPin;
93    type Reset: OutputPin;
94    type Busy: InputPin + Wait;
95    type Delay: DelayNs;
96    type Error: CoreError
97        + From<<Self::Spi as SpiErrorType>::Error>
98        + From<<Self::Dc as PinErrorType>::Error>
99        + From<<Self::Reset as PinErrorType>::Error>
100        + From<<Self::Busy as PinErrorType>::Error>
101        + From<crate::Error>;
102
103    fn dc(&mut self) -> &mut Self::Dc;
104    fn reset(&mut self) -> &mut Self::Reset;
105    fn busy(&mut self) -> &mut Self::Busy;
106    fn delay(&mut self) -> &mut Self::Delay;
107}
108
109/// Provides "wait" support for hardware with a busy state.
110pub(crate) trait BusyWait: EpdHw {
111    /// Waits for the current operation to complete if the display is busy.
112    ///
113    /// Note that this will wait forever if the display is asleep.
114    async fn wait_if_busy(&mut self) -> Result<(), Self::Error>;
115}
116
117/// Provides the ability to send <command> then <data> style communications.
118pub(crate) trait CommandDataSend: EpdHw {
119    /// Send the following command and data to the display. Waits until the display is no longer busy before sending.
120    async fn send(
121        &mut self,
122        spi: &mut <Self as EpdHw>::Spi,
123        command: u8,
124        data: &[u8],
125    ) -> Result<(), Self::Error>;
126}
127
128impl<HW: EpdHw> BusyWait for HW {
129    async fn wait_if_busy(&mut self) -> Result<(), HW::Error> {
130        let busy = self.busy();
131        // Note: the datasheet states that busy pin is active low, i.e. we should wait for it when
132        // it's low, but this is incorrect. The sample code treats it as active high, which works.
133        if busy.is_high().unwrap() {
134            trace!("Waiting for busy EPD");
135            busy.wait_for_low().await?;
136        }
137        Ok(())
138    }
139}
140
141impl<HW: EpdHw> CommandDataSend for HW {
142    async fn send(
143        &mut self,
144        spi: &mut <Self as EpdHw>::Spi,
145        command: u8,
146        data: &[u8],
147    ) -> Result<(), Self::Error> {
148        trace!("Sending EPD command: {:?}", command);
149        self.wait_if_busy().await?;
150
151        self.dc().set_low()?;
152        spi.write(&[command]).await?;
153
154        if !data.is_empty() {
155            self.dc().set_high()?;
156            spi.write(data).await?;
157        }
158
159        Ok(())
160    }
161}