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 176 lines 6.5 kB view raw
1//! Bitbanging driver for the SPI-derived protocol used by Pervasive Displays e-paper screens. 2 3#![no_std] 4 5#[cfg(feature = "rp2040")] 6pub mod rp2040; 7 8use embedded_hal::digital::{InputPin, OutputPin, PinState}; 9 10/// A trait for pin types that allows running a closure with the pin configured as an input. 11pub trait WithInput { 12 /// The type of the pin after it has been reconfigured to an input 13 type Input: InputPin; 14 15 /// Run the provided closure with the pin configured as an input. 16 fn with_input<R>(&mut self, f: impl Fn(&mut Self::Input) -> R) -> R; 17} 18 19/// A trait for pin types that allows running a closure with the pin configured as an output. 20pub trait WithOutput { 21 /// The type of the pin after it has been reconfigured to an output 22 type Output: OutputPin; 23 24 /// Run the provided closure with the pin configured as an output. 25 fn with_output<R>(&mut self, f: impl Fn(&mut Self::Output) -> R) -> R; 26} 27 28/// A trait defining delays to be inserted at specific points in the bitbanging process. 29/// 30/// `DelayNs` is not used, because the delays involved are likely to be very short (up to and 31/// including a single NOP). It is intended that these functions be implemented with NOPs or 32/// functions such as `cortex_m::asm::delay`. Some of these delays may not be required on your 33/// hardware at all, in which case you can leave the implementation blank. It is recommended to 34/// mark implementations as `#[inline(always)]`. 35/// 36/// An implementation designed for use with the RP2040 with the TP370PGH01 display is provided 37/// in the `tp370pgh01` crate (with the `rp2040` feature enabled). 38pub trait PervasiveSpiDelays { 39 fn delay_read_after_sck_high(&self); 40 fn delay_read_after_sck_low(&self); 41 fn delay_read_after_byte(&self); 42 43 fn delay_write_after_sda_set(&self); 44 fn delay_write_after_sck_high(&self); 45 fn delay_write_after_sck_low(&self); 46 fn delay_write_after_byte(&self); 47} 48 49/// A bitbanging driver for the SPI-derived protocol used by Pervasive Displays e-paper screens. 50pub struct PervasiveSpi<Cs, Sda, Sck, Dc, Delay> { 51 cs: Cs, 52 sda: Sda, 53 sck: Sck, 54 dc: Dc, 55 delay: Delay, 56} 57 58impl<Cs, Sda, Sck, Dc, Delay, Error> PervasiveSpi<Cs, Sda, Sck, Dc, Delay> 59where 60 Cs: OutputPin<Error = Error>, 61 Sda: WithInput + WithOutput, 62 <Sda as WithInput>::Input: InputPin<Error = Error>, 63 <Sda as WithOutput>::Output: OutputPin<Error = Error>, 64 Sck: OutputPin<Error = Error>, 65 Dc: OutputPin<Error = Error>, 66 Delay: PervasiveSpiDelays, 67{ 68 /// Create a new instance. 69 /// 70 /// * `cs`: The chip select pin (output). 71 /// * `sda`: The SDA pin. The type must implement both the `WithInput` and `WithOutput` traits. 72 /// If the `rp2040` feature is enabled, the [rp2040] module provides an `IoPin` type 73 /// implementing both of these for the RP2040. 74 /// * `scl`: The SCL pin (output). 75 /// * `dc`: The D/C (data/command) pin (output). 76 /// * `delay`: An object implementing `PervasiveSpiDelays`, describing delays to be inserted at 77 /// particular points in the bitbanging process. 78 pub fn new(cs: Cs, sda: Sda, sck: Sck, dc: Dc, delay: Delay) -> Self { 79 Self { 80 cs, 81 sda, 82 sck, 83 dc, 84 delay, 85 } 86 } 87 88 fn _write(&mut self, data: &[u8], register: bool, reverse_bits: bool) -> Result<(), Error> { 89 if register { 90 self.dc.set_low()?; 91 } else { 92 self.dc.set_high()?; 93 } 94 95 for &byte in data { 96 self.cs.set_low()?; 97 98 for i in 0..8 { 99 let bit = if reverse_bits { 100 byte & (1 << i) 101 } else { 102 byte & (1 << (7 - i)) 103 }; 104 let state = PinState::from(bit != 0); 105 106 self.sda.with_output(|sda| sda.set_state(state))?; 107 self.delay.delay_write_after_sda_set(); 108 self.sck.set_high()?; 109 self.delay.delay_write_after_sck_high(); 110 self.sck.set_low()?; 111 self.delay.delay_write_after_sck_low(); 112 } 113 114 self.dc.set_high()?; 115 self.cs.set_high()?; 116 self.delay.delay_write_after_byte(); 117 } 118 119 Ok(()) 120 } 121 122 /// Write the contents of `data` to the display, treating the first bytes in `data` as the 123 /// register number. The D/C pin will be held low when sending the first byte, and high for 124 /// the remaining bytes. 125 pub fn write_register(&mut self, data: &[u8]) -> Result<(), Error> { 126 self._write(data, true, false) 127 } 128 129 /// Write the contents of `data` to the display. All bytes in `data` are treated as data, i.e. 130 /// the D/C pin will remain high for all bytes. 131 pub fn write_data(&mut self, data: &[u8]) -> Result<(), Error> { 132 self._write(data, false, false) 133 } 134 135 /// Write the contents of `data` to the display, reversing the order of the bits in each byte. 136 /// 137 /// This is useful for sending image data, as the more common way of encoding raw 1-bit mono 138 /// images (e.g. with ImageMagick's MONO format) uses the opposite bit order from what Pervasive 139 /// Displays screens expect. 140 pub fn write_data_reversed(&mut self, data: &[u8]) -> Result<(), Error> { 141 self._write(data, false, true) 142 } 143 144 /// Read data from the display into `buf`. 145 /// 146 /// All bytes in `buf` will be filled. It is the caller's responsibility to make sure `buf` is 147 /// not longer than the number of bytes they want to read. 148 pub fn read(&mut self, buf: &mut [u8]) -> Result<(), Error> { 149 for byte in buf.iter_mut() { 150 self.cs.set_low()?; 151 152 for i in 0..8 { 153 self.sck.set_high()?; 154 self.delay.delay_read_after_sck_high(); 155 *byte |= (self.sda.with_input(|sda| sda.is_high())? as u8) << (7 - i); 156 self.sck.set_low()?; 157 self.delay.delay_read_after_sck_low(); 158 } 159 160 self.cs.set_high()?; 161 self.delay.delay_read_after_byte(); 162 } 163 164 Ok(()) 165 } 166 167 /// Manually set the chip-select pin high. This is intended for performing hard display resets. 168 pub fn set_cs_high(&mut self) -> Result<(), Error> { 169 self.cs.set_high() 170 } 171 172 /// Manually et the chip-select pin low. This is intended for performing hard display resets. 173 pub fn set_cs_low(&mut self) -> Result<(), Error> { 174 self.cs.set_low() 175 } 176}