···11+# Generated by Cargo
22+# will have compiled files and executables
33+debug/
44+target/
55+66+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
77+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
88+Cargo.lock
99+1010+# These are backup files generated by rustfmt
1111+**/*.rs.bk
1212+1313+# MSVC Windows builds of rustc generate these, which store debugging information
1414+*.pdb
1515+1616+# RustRover
1717+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
1818+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
1919+# and can be added to the global gitignore or merged into this file. For a more nuclear
2020+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
2121+#.idea/
···11+//! Bitbanging driver for the SPI-derived protocol used by Pervasive Displays e-paper screens.
22+33+#![no_std]
44+55+#[cfg(feature = "rp2040")]
66+pub mod rp2040;
77+88+use defmt::trace;
99+use embedded_hal::delay::DelayNs;
1010+use embedded_hal::digital::{InputPin, OutputPin, PinState};
1111+1212+/// A trait for pin types that allows running a closure with the pin configured as an input.
1313+pub trait WithInput {
1414+ /// The type of the pin after it has been reconfigured to an input
1515+ type Input: InputPin;
1616+1717+ /// Run the provided closure with the pin configured as an input.
1818+ fn with_input<R>(&mut self, f: impl Fn(&mut Self::Input) -> R) -> R;
1919+}
2020+2121+/// A trait for pin types that allows running a closure with the pin configured as an output.
2222+pub trait WithOutput {
2323+ /// The type of the pin after it has been reconfigured to an output
2424+ type Output: OutputPin;
2525+2626+ /// Run the provided closure with the pin configured as an output.
2727+ fn with_output<R>(&mut self, f: impl Fn(&mut Self::Output) -> R) -> R;
2828+}
2929+3030+/// A trait defining delays to be inserted at specific points in the bitbanging process.
3131+///
3232+/// `DelayNs` is not used, because the delays involved are likely to be very short (up to and
3333+/// including a single NOP). It is intended that these functions be implemented with NOPs or
3434+/// functions such as `cortex_m::asm::delay`. Some of these delays may not be required on your
3535+/// hardware at all, in which case you can leave the implementation blank. It is recommended to
3636+/// mark implementations as `#[inline(always)]`.
3737+///
3838+/// An implementation designed for use with the RP2040 with the TP370PGH01 display is provided
3939+/// in the `tp370pgh01` crate (with the `rp2040` feature enabled).
4040+pub trait PervasiveSpiDelays {
4141+ fn delay_read_after_sck_high(&self);
4242+ fn delay_read_after_sck_low(&self);
4343+ fn delay_read_after_byte(&self);
4444+4545+ fn delay_write_after_sda_set(&self);
4646+ fn delay_write_after_sck_high(&self);
4747+ fn delay_write_after_sck_low(&self);
4848+ fn delay_write_after_byte(&self);
4949+}
5050+5151+/// A bitbanging driver for the SPI-derived protocol used by Pervasive Displays e-paper screens.
5252+pub struct PervasiveSpi<Cs, Sda, Sck, Dc, Delay> {
5353+ cs: Cs,
5454+ sda: Sda,
5555+ sck: Sck,
5656+ dc: Dc,
5757+ delay: Delay,
5858+}
5959+6060+impl<Cs, Sda, Sck, Dc, Delay, Error> PervasiveSpi<Cs, Sda, Sck, Dc, Delay>
6161+where
6262+ Cs: OutputPin<Error = Error>,
6363+ Sda: WithInput + WithOutput,
6464+ <Sda as WithInput>::Input: InputPin<Error = Error>,
6565+ <Sda as WithOutput>::Output: OutputPin<Error = Error>,
6666+ Sck: OutputPin<Error = Error>,
6767+ Dc: OutputPin<Error = Error>,
6868+ Delay: PervasiveSpiDelays,
6969+{
7070+ /// Create a new instance.
7171+ ///
7272+ /// * `cs`: The chip select pin (output).
7373+ /// * `sda`: The SDA pin. The type must implement both the `WithInput` and `WithOutput` traits.
7474+ /// An implementation for both of these on the RP2040 is provided if the `rp2040`
7575+ /// feature is enabled. For internal reasons, the trait is implemented on `Option<Pin>`
7676+ /// instead of `Pin` on the RP2040, but reading and writing will panic if `None` is
7777+ /// provided.
7878+ /// * `scl`: The SCL pin (output).
7979+ /// * `dc`: The D/C (data/command) pin (output).
8080+ /// * `delay`: An object implementing `PervasiveSpiDelays`, describing delays to be inserted at
8181+ /// particular points in the bitbanging process.
8282+ pub fn new(cs: Cs, sda: Sda, sck: Sck, dc: Dc, delay: Delay) -> Self {
8383+ Self {
8484+ cs,
8585+ sda,
8686+ sck,
8787+ dc,
8888+ delay,
8989+ }
9090+ }
9191+9292+ fn _write(&mut self, data: &[u8], register: bool, reverse_bits: bool) -> Result<(), Error> {
9393+ if register {
9494+ self.dc.set_low()?;
9595+ } else {
9696+ self.dc.set_high()?;
9797+ }
9898+9999+ for &(mut byte) in data {
100100+ self.cs.set_low()?;
101101+102102+ for i in 0..8 {
103103+ let bit = if reverse_bits {
104104+ byte & (1 << i)
105105+ } else {
106106+ byte & (1 << (7 - i))
107107+ };
108108+ let state = PinState::from(bit != 0);
109109+110110+ self.sda.with_output(|sda| sda.set_state(state))?;
111111+ self.delay.delay_write_after_sda_set();
112112+ self.sck.set_high()?;
113113+ self.delay.delay_write_after_sck_high();
114114+ self.sck.set_low()?;
115115+ self.delay.delay_write_after_sck_low();
116116+ }
117117+118118+ self.dc.set_high()?;
119119+ self.cs.set_high()?;
120120+ self.delay.delay_write_after_byte();
121121+ }
122122+123123+ Ok(())
124124+ }
125125+126126+ /// Write the contents of `data` to the display, treating the first bytes in `data` as the
127127+ /// register number. The D/C pin will be held low when sending the first byte, and high for
128128+ /// the remaining bytes.
129129+ pub fn write_register(&mut self, data: &[u8]) -> Result<(), Error> {
130130+ self._write(data, true, false)
131131+ }
132132+133133+ /// Write the contents of `data` to the display. All bytes in `data` are treated as data, i.e.
134134+ /// the D/C pin will remain high for all bytes.
135135+ pub fn write_data(&mut self, data: &[u8]) -> Result<(), Error> {
136136+ self._write(data, false, false)
137137+ }
138138+139139+ /// Write the contents of `data` to the display, reversing the order of the bits in each byte.
140140+ ///
141141+ /// This is useful for sending image data, as the more common way of encoding raw 1-bit mono
142142+ /// images (e.g. with ImageMagick's MONO format) uses the opposite bit order from what Pervasive
143143+ /// Displays screens expect.
144144+ pub fn write_data_reversed(&mut self, data: &[u8]) -> Result<(), Error> {
145145+ self._write(data, false, true)
146146+ }
147147+148148+ /// Read data from the display into `buf`.
149149+ ///
150150+ /// All bytes in `buf` will be filled. It is the caller's responsibility to make sure `buf` is
151151+ /// not longer than the number of bytes they want to read.
152152+ pub fn read(&mut self, buf: &mut [u8]) -> Result<(), Error> {
153153+ for byte in buf.iter_mut() {
154154+ self.cs.set_low()?;
155155+156156+ for i in 0..8 {
157157+ self.sck.set_high()?;
158158+ self.delay.delay_read_after_sck_high();
159159+ *byte |= (self.sda.with_input(|sda| sda.is_high())? as u8) << (7 - i);
160160+ self.sck.set_low()?;
161161+ self.delay.delay_read_after_sck_low();
162162+ }
163163+164164+ trace!("read byte {}", byte);
165165+166166+ self.cs.set_high()?;
167167+ self.delay.delay_read_after_byte();
168168+ }
169169+170170+ Ok(())
171171+ }
172172+173173+ /// Manually set the chip-select pin high. This is intended for performing hard display resets.
174174+ pub fn set_cs_high(&mut self) -> Result<(), Error> {
175175+ self.cs.set_high()
176176+ }
177177+178178+ /// Manually et the chip-select pin low. This is intended for performing hard display resets.
179179+ pub fn set_cs_low(&mut self) -> Result<(), Error> {
180180+ self.cs.set_low()
181181+ }
182182+}
+36
pervasive-spi/src/rp2040.rs
···11+use crate::{WithInput, WithOutput};
22+use rp2040_hal::gpio::{
33+ Function, FunctionSioInput, FunctionSioOutput, Pin, PinId, PullType, ValidFunction,
44+};
55+66+impl<I, F, P> WithInput for Option<Pin<I, F, P>>
77+where
88+ I: PinId + ValidFunction<FunctionSioInput> + ValidFunction<F>,
99+ F: Function,
1010+ P: PullType,
1111+{
1212+ type Input = Pin<I, FunctionSioInput, P>;
1313+1414+ fn with_input<R>(&mut self, f: impl Fn(&mut Self::Input) -> R) -> R {
1515+ let mut input = self.take().unwrap().reconfigure();
1616+ let res = f(&mut input);
1717+ self.replace(input.reconfigure());
1818+ res
1919+ }
2020+}
2121+2222+impl<I, F, P> WithOutput for Option<Pin<I, F, P>>
2323+where
2424+ I: PinId + ValidFunction<FunctionSioOutput> + ValidFunction<F>,
2525+ F: Function,
2626+ P: PullType,
2727+{
2828+ type Output = Pin<I, FunctionSioOutput, P>;
2929+3030+ fn with_output<R>(&mut self, f: impl Fn(&mut Self::Output) -> R) -> R {
3131+ let mut output = self.take().unwrap().reconfigure();
3232+ let res = f(&mut output);
3333+ self.replace(output.reconfigure());
3434+ res
3535+ }
3636+}