firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
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}