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