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}