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

Configure Feed

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

at a3ff1f4e4e2aaec4a4c1ec5def91c21d36dc12f7 320 lines 11 kB view raw
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}