firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
0

Configure Feed

Select the types of activity you want to include in your feed.

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}