firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
1//! Bitbanging driver for the Pervasive Displays TP370PGH01 display.
2
3#![no_std]
4
5#[cfg(feature = "rp2040")]
6pub mod rp2040;
7
8#[cfg(feature = "defmt")]
9use defmt::{debug, error, trace};
10
11use core::error::Error;
12use core::fmt::{Debug, Display, Formatter};
13use embedded_hal::delay::DelayNs;
14use embedded_hal::digital::{InputPin, OutputPin};
15use pervasive_spi::{PervasiveSpi, PervasiveSpiDelays, WithInput, WithOutput};
16
17/// The horizontal size of the display in pixels.
18pub const DIM_X: usize = 240;
19/// The vertical size of the display in pixels.
20pub const DIM_Y: usize = 416;
21/// The number of bytes in an image frame sent to the display, equal to `(DIM_X * DIM_Y) / 8`.
22pub const IMAGE_BYTES: usize = (DIM_X * DIM_Y) / 8;
23
24/// An error that can occur communicating with the display.
25pub enum Tp370pgh01Error<GE> {
26 /// An error occured reading or setting the GPIO pins,
27 GpioError(GE),
28
29 /// Reading the PSR value from the display's OTP memory failed.
30 ///
31 /// This usually indicates that there is a problem with the connection to the display, or
32 /// not enough delays are being inserted.
33 ReadPsrInvalid,
34}
35
36impl<GE> From<GE> for Tp370pgh01Error<GE> {
37 fn from(value: GE) -> Self {
38 Self::GpioError(value)
39 }
40}
41
42impl<GE: Error> Debug for Tp370pgh01Error<GE> {
43 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
44 match self {
45 Self::GpioError(e) => write!(f, "GPIO error: {e:?}"),
46 Self::ReadPsrInvalid => write!(f, "Could not find PSR in display OTP memory"),
47 }
48 }
49}
50
51impl<GE: Error> Display for Tp370pgh01Error<GE> {
52 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
53 match self {
54 Self::GpioError(e) => write!(f, "GPIO error: {e}"),
55 Self::ReadPsrInvalid => write!(f, "Could not find PSR in display OTP memory"),
56 }
57 }
58}
59
60impl<GE: Error> Error for Tp370pgh01Error<GE> {}
61
62/// A bitbanging driver for the Pervasive Displays TP370PGH01 display.
63pub struct Tp370pgh01<Cs, Sda, Sck, Dc, Busy, Reset, Delay, SpiDelay> {
64 spi: PervasiveSpi<Cs, Sda, Sck, Dc, SpiDelay>,
65 busy: Busy,
66 reset: Reset,
67 delay: Delay,
68 psr: Option<[u8; 2]>,
69}
70
71impl<Cs, Sda, Sck, Dc, Busy, Reset, Delay, SpiDelay, Error>
72 Tp370pgh01<Cs, Sda, Sck, Dc, Busy, Reset, Delay, SpiDelay>
73where
74 Cs: OutputPin<Error = Error>,
75 Sda: WithInput + WithOutput,
76 <Sda as WithInput>::Input: InputPin<Error = Error>,
77 <Sda as WithOutput>::Output: OutputPin<Error = Error>,
78 Sck: OutputPin<Error = Error>,
79 Dc: OutputPin<Error = Error>,
80 Busy: InputPin<Error = Error>,
81 Reset: OutputPin<Error = Error>,
82 Delay: DelayNs,
83 SpiDelay: PervasiveSpiDelays,
84{
85 /// Create a new instance of the driver.
86 ///
87 /// * `cs` - Chip select pin (output).
88 /// * `sda` - SDA pin (bidirectional). This must implement both the `WithInput` and `WithOutput`
89 /// traits from the `pervasive-spi` crate. That crate provides a type implementing
90 /// these traits for the RP2040 if the `rp2040` feature is enabled.
91 /// * `sck` - SCK pin (output).
92 /// * `dc` - D/C (data/command) pin (output).
93 /// * `reset` - Display reset pin (output).
94 /// * `delay` - An instance of `DelayNs`, used for timing some display commands such as resets.
95 /// * `spi_delay` - An instance of `PervasiveSpiDelays` from the `pervasive-spi` crate. This
96 /// crate provides an implementation for the RP2040 with this display if the
97 /// `rp2040` feature is activated.
98 pub fn new(
99 cs: Cs,
100 sda: Sda,
101 sck: Sck,
102 dc: Dc,
103 busy: Busy,
104 reset: Reset,
105 delay: Delay,
106 spi_delay: SpiDelay,
107 ) -> Self {
108 let spi = PervasiveSpi::new(cs, sda, sck, dc, spi_delay);
109 Self {
110 spi,
111 busy,
112 reset,
113 delay,
114 psr: None,
115 }
116 }
117
118 /// Hard-reset the display using the reset pin, then perform a soft reset.
119 pub fn hard_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
120 #[cfg(feature = "defmt")]
121 debug!("tp370pgh01: hard resetting display");
122 self.spi.set_cs_low()?;
123 self.reset.set_high()?;
124 self.delay.delay_ms(5);
125 self.reset.set_low()?;
126 self.delay.delay_ms(10);
127 self.reset.set_high()?;
128 self.delay.delay_ms(5);
129 self.spi.set_cs_high()?;
130 self.soft_reset()?;
131 #[cfg(feature = "defmt")]
132 debug!("tp370pgh01: hard reset display");
133
134 Ok(())
135 }
136
137 /// Perform a soft reset.
138 pub fn soft_reset(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
139 #[cfg(feature = "defmt")]
140 debug!("tp370pgh01: soft resetting display");
141 self.spi.write_register(&[0x00, 0x0e])?;
142 self.delay.delay_ms(5);
143 #[cfg(feature = "defmt")]
144 debug!("tp370pgh01: soft reset display");
145 Ok(())
146 }
147
148 fn get_psr(&mut self) -> Result<[u8; 2], Tp370pgh01Error<Error>> {
149 if let Some(psr) = self.psr {
150 return Ok(psr);
151 }
152
153 #[cfg(feature = "defmt")]
154 debug!("tp370pgh01: reading PSR");
155 self.spi.write_register(&[0xa2])?;
156 let mut buf = [0u8; 2];
157 self.delay.delay_ms(10);
158 self.spi.read(&mut buf)?;
159
160 let bank0 = buf[1] == 0xa5;
161 #[cfg(feature = "defmt")]
162 debug!(
163 "tp370pgh01: PSR is in bank {}",
164 if bank0 { '0' } else { '1' }
165 );
166
167 let offset_psr: u16 = if bank0 { 0x0fb4 } else { 0x1fb4 };
168 let offset_a5: u16 = if bank0 { 0x0000 } else { 0x1000 };
169
170 if offset_a5 > 0 {
171 for _ in 1..offset_a5 {
172 self.spi.read(&mut [0])?;
173 }
174
175 let mut buf = [0];
176 self.spi.read(&mut buf)?;
177 if buf[0] != 0xa5 {
178 #[cfg(feature = "defmt")]
179 error!("tp370pgh01: failed to find PSR");
180 return Err(Tp370pgh01Error::ReadPsrInvalid);
181 }
182 }
183
184 for _ in offset_a5 + 1..offset_psr {
185 self.spi.read(&mut [0])?;
186 }
187
188 self.spi.read(&mut buf)?;
189 #[cfg(feature = "defmt")]
190 debug!("tp370pgh01: found PSR: {} {}", buf[0], buf[1]);
191
192 self.psr.replace(buf);
193
194 Ok(buf)
195 }
196
197 fn busy_wait(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
198 #[cfg(feature = "defmt")]
199 trace!("tp370pgh01: busy waiting");
200 while self.busy.is_low()? {}
201 Ok(())
202 }
203
204 fn trigger_refresh(&mut self) -> Result<(), Tp370pgh01Error<Error>> {
205 #[cfg(feature = "defmt")]
206 debug!("tp370pgh01: turning on DC/DC");
207 // turn on DC/DC
208 self.spi.write_register(&[0x04])?;
209 self.busy_wait()?;
210 // refresh
211 #[cfg(feature = "defmt")]
212 debug!("tp370pgh01: refreshing");
213 self.spi.write_register(&[0x12])?;
214 self.busy_wait()?;
215 // turn off DC/DC
216 #[cfg(feature = "defmt")]
217 debug!("tp370pgh01: turning off DC/DC");
218 self.spi.write_register(&[0x02])?;
219 self.busy_wait()?;
220
221 Ok(())
222 }
223
224 /// Refresh the display using a normal (non-fast) refresh.
225 ///
226 /// This type of refresh takes a couple of seconds, and the screen will flash between black and
227 /// white multiple times before the image appears.
228 ///
229 /// * `image` - The image data to write to the display.
230 /// * `temperature` - The ambient temperature in degrees Celsius. According to the display
231 /// datasheet, this should be clamped between 0 and 60 degrees.
232 pub fn refresh(
233 &mut self,
234 image: &[u8; IMAGE_BYTES],
235 temperature: u8,
236 ) -> Result<(), Tp370pgh01Error<Error>> {
237 #[cfg(feature = "defmt")]
238 debug!("tp370pgh01: begin normal refresh");
239
240 let psr = self.get_psr()?;
241
242 // set temperature
243 self.spi.write_register(&[0xe5, temperature])?;
244 self.spi.write_register(&[0xe0, 0x02])?;
245
246 // set PSR
247 self.spi.write_register(&[0x00])?;
248 self.spi.write_data(&psr)?;
249
250 // write image data
251 #[cfg(feature = "defmt")]
252 debug!("tp370pgh01: writing image");
253 self.spi.write_register(&[0x10])?;
254 self.spi.write_data_reversed(image)?;
255 // write dummy data
256 self.spi.write_register(&[0x13])?;
257 for _ in 0..IMAGE_BYTES {
258 self.spi.write_data(&[0x00])?;
259 }
260 #[cfg(feature = "defmt")]
261 debug!("tp370pgh01: finished writing image");
262
263 self.trigger_refresh()?;
264
265 #[cfg(feature = "defmt")]
266 debug!("tp370pgh01: end normal refresh");
267 Ok(())
268 }
269
270 /// Refresh the display using a fast refresh.
271 ///
272 /// This type of refresh takes around half a second and does not flash the screen, but is
273 /// susceptible to ghosting.
274 ///
275 /// * `image` - The image data to write to the display.
276 /// * `prev_image` The image data currently being displayed.
277 /// * `temperature` - The ambient temperature in degrees Celsius. According to the display
278 /// datasheet, this should be clamped between 0 and 60 degrees.
279 pub fn refresh_fast(
280 &mut self,
281 image: &[u8; IMAGE_BYTES],
282 prev_image: &[u8; IMAGE_BYTES],
283 temperature: u8,
284 ) -> Result<(), Tp370pgh01Error<Error>> {
285 #[cfg(feature = "defmt")]
286 debug!("tp370pgh01: begin fast refresh");
287
288 let psr = self.get_psr()?;
289
290 // set temperature
291 self.spi.write_register(&[0xe5, temperature | 0x40])?;
292 self.spi.write_register(&[0xe0, 0x02])?;
293 // set PSR
294 self.spi
295 .write_register(&[0x00, psr[0] | 0x10, psr[1] | 0x02])?;
296 // set "Vcom and data interval setting"
297 self.spi.write_register(&[0x50, 0x07])?;
298
299 // set "border setting"
300 self.spi.write_register(&[0x50, 0x27])?;
301 // send previous image
302 #[cfg(feature = "defmt")]
303 debug!("tp370pgh01: writing image");
304 self.spi.write_register(&[0x10])?;
305 self.spi.write_data_reversed(prev_image)?;
306 // send new image
307 self.spi.write_register(&[0x13])?;
308 self.spi.write_data_reversed(image)?;
309 #[cfg(feature = "defmt")]
310 debug!("tp370pgh01: finished writing image");
311 // set "border setting"
312 self.spi.write_register(&[0x50, 0x07])?;
313
314 self.trigger_refresh()?;
315
316 #[cfg(feature = "defmt")]
317 debug!("tp370pgh01: end fast refresh");
318 Ok(())
319 }
320}