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 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}