Skip to main content

lcd_async/interface/
parallel.rs

1use embedded_hal::digital::OutputPin;
2
3use super::{Interface, InterfaceKind};
4
5/// This trait represents the data pins of a parallel bus.
6///
7/// See [Generic8BitBus] and [Generic16BitBus] for generic implementations.
8pub trait OutputBus {
9    /// [u8] for 8-bit buses, [u16] for 16-bit buses, etc.
10    type Word: Copy;
11
12    /// Interface kind.
13    const KIND: InterfaceKind;
14
15    /// Error type
16    type Error: core::fmt::Debug;
17
18    /// Set the output bus to a specific value
19    fn set_value(&mut self, value: Self::Word) -> Result<(), Self::Error>;
20}
21
22macro_rules! generic_bus {
23    ($GenericxBitBus:ident { type Word = $Word:ident; const KIND: InterfaceKind = $InterfaceKind:expr; Pins {$($PX:ident => $x:tt,)*}}) => {
24        /// A generic implementation of [OutputBus] using [OutputPin]s
25        pub struct $GenericxBitBus<$($PX, )*> {
26            pins: ($($PX, )*),
27            last: Option<$Word>,
28        }
29
30        impl<$($PX, )*> $GenericxBitBus<$($PX, )*>
31        where
32            $($PX: OutputPin, )*
33        {
34            /// Creates a new bus. This does not change the state of the pins.
35            ///
36            /// The first pin in the tuple is the least significant bit.
37            pub fn new(pins: ($($PX, )*)) -> Self {
38                Self { pins, last: None }
39            }
40
41            /// Consumes the bus and returns the pins. This does not change the state of the pins.
42            pub fn release(self) -> ($($PX, )*) {
43                self.pins
44            }
45        }
46
47        impl<$($PX, )* E> OutputBus
48            for $GenericxBitBus<$($PX, )*>
49        where
50            $($PX: OutputPin<Error = E>, )*
51            E: core::fmt::Debug,
52        {
53            type Word = $Word;
54            type Error = E;
55
56            const KIND: InterfaceKind = $InterfaceKind;
57
58            fn set_value(&mut self, value: Self::Word) -> Result<(), Self::Error> {
59                // It's quite common for multiple consecutive values to be identical, e.g. when filling or
60                // clearing the screen, so let's optimize for that case.
61                // The `Eq` bound for this is on the `ParallelInterface` impl.
62                if self.last == Some(value) {
63                    return Ok(())
64                }
65
66                // Sets self.last to None.
67                // We will update it to Some(value) *after* all the pins are succesfully set.
68                let last = self.last.take();
69
70                let changed = match last {
71                    Some(old_value) => value ^ old_value,
72                    None => !0, // all ones, this ensures that we will update all the pins
73                };
74
75                $(
76                    let mask = 1 << $x;
77                    if changed & mask != 0 {
78                        if value & mask != 0 {
79                            self.pins.$x.set_high()
80                        } else {
81                            self.pins.$x.set_low()
82                        }
83                        ?;
84                    }
85                )*
86
87                self.last = Some(value);
88                Ok(())
89            }
90        }
91
92        impl<$($PX, )*> From<($($PX, )*)>
93            for $GenericxBitBus<$($PX, )*>
94        where
95            $($PX: OutputPin, )*
96        {
97            fn from(pins: ($($PX, )*)) -> Self {
98                Self::new(pins)
99            }
100        }
101    };
102}
103
104generic_bus! {
105    Generic8BitBus {
106        type Word = u8;
107        const KIND: InterfaceKind = InterfaceKind::Parallel8Bit;
108        Pins {
109            P0 => 0,
110            P1 => 1,
111            P2 => 2,
112            P3 => 3,
113            P4 => 4,
114            P5 => 5,
115            P6 => 6,
116            P7 => 7,
117        }
118    }
119}
120
121generic_bus! {
122    Generic16BitBus {
123        type Word = u16;
124        const KIND: InterfaceKind = InterfaceKind::Parallel16Bit;
125        Pins {
126            P0 => 0,
127            P1 => 1,
128            P2 => 2,
129            P3 => 3,
130            P4 => 4,
131            P5 => 5,
132            P6 => 6,
133            P7 => 7,
134            P8 => 8,
135            P9 => 9,
136            P10 => 10,
137            P11 => 11,
138            P12 => 12,
139            P13 => 13,
140            P14 => 14,
141            P15 => 15,
142        }
143    }
144}
145
146/// Parallel interface error
147#[derive(Clone, Copy, Debug)]
148#[cfg_attr(feature = "defmt", derive(defmt::Format))]
149pub enum ParallelError<BUS, DC, WR> {
150    /// Bus error
151    Bus(BUS),
152    /// Data/command pin error
153    Dc(DC),
154    /// Write pin error
155    Wr(WR),
156}
157
158/// Parallel communication interface
159///
160/// This interface implements a "8080" style write-only display interface using any
161/// [`OutputBus`] implementation as well as one
162/// [`OutputPin`] for the data/command selection and one [`OutputPin`] for the write-enable flag.
163///
164/// All pins in the data bus are supposed to be high-active. High for the D/C pin meaning "data" and the
165/// write-enable being pulled low before the setting of the bits and supposed to be sampled at a
166/// low to high edge.
167pub struct ParallelInterface<BUS, DC, WR> {
168    bus: BUS,
169    dc: DC,
170    wr: WR,
171}
172
173impl<BUS, DC, WR> ParallelInterface<BUS, DC, WR>
174where
175    BUS: OutputBus,
176    // The Eq bound is used by the `set_value` optimization in the generic bus
177    BUS::Word: From<u8> + Eq + core::ops::BitXor<Output = BUS::Word>,
178    DC: OutputPin,
179    WR: OutputPin,
180{
181    /// Create new parallel GPIO interface for communication with a display driver
182    pub fn new(bus: BUS, dc: DC, wr: WR) -> Self {
183        Self { bus, dc, wr }
184    }
185
186    /// Consume the display interface and return
187    /// the bus and GPIO pins used by it
188    pub fn release(self) -> (BUS, DC, WR) {
189        (self.bus, self.dc, self.wr)
190    }
191
192    /// Sends a single word to the display.
193    fn send_word(
194        &mut self,
195        word: BUS::Word,
196    ) -> Result<(), ParallelError<BUS::Error, DC::Error, WR::Error>> {
197        self.wr.set_low().map_err(ParallelError::Wr)?;
198        self.bus.set_value(word).map_err(ParallelError::Bus)?;
199        self.wr.set_high().map_err(ParallelError::Wr)
200    }
201}
202
203impl<BUS, DC, WR> Interface for ParallelInterface<BUS, DC, WR>
204where
205    BUS: OutputBus,
206    // The Eq bound is used by the `set_value` optimization in the generic bus.
207    // The BitXor is also needed for that optimization.
208    BUS::Word: From<u8> + Eq + core::ops::BitXor<Output = BUS::Word>,
209    DC: OutputPin,
210    WR: OutputPin,
211{
212    type Word = BUS::Word;
213    type Error = ParallelError<BUS::Error, DC::Error, WR::Error>;
214
215    const KIND: InterfaceKind = BUS::KIND;
216
217    async fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> {
218        // Set DC pin low for command
219        self.dc.set_low().map_err(ParallelError::Dc)?;
220        self.send_word(BUS::Word::from(command))?;
221
222        // Set DC pin high for data
223        self.dc.set_high().map_err(ParallelError::Dc)?;
224        for &arg in args {
225            self.send_word(BUS::Word::from(arg))?;
226        }
227
228        Ok(())
229    }
230
231    async fn send_data_slice(&mut self, data: &[Self::Word]) -> Result<(), Self::Error> {
232        // DC pin is expected to be high (data mode) from a previous command.
233        // We just need to send the words.
234        for &word in data {
235            self.send_word(word)?;
236        }
237        Ok(())
238    }
239}