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