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)]
148pub enum ParallelError<BUS, DC, WR> {
149    /// Bus error
150    Bus(BUS),
151    /// Data/command pin error
152    Dc(DC),
153    /// Write pin error
154    Wr(WR),
155}
156
157/// Parallel communication interface
158///
159/// This interface implements a "8080" style write-only display interface using any
160/// [`OutputBus`] implementation as well as one
161/// [`OutputPin`] for the data/command selection and one [`OutputPin`] for the write-enable flag.
162///
163/// All pins in the data bus are supposed to be high-active. High for the D/C pin meaning "data" and the
164/// write-enable being pulled low before the setting of the bits and supposed to be sampled at a
165/// low to high edge.
166pub struct ParallelInterface<BUS, DC, WR> {
167    bus: BUS,
168    dc: DC,
169    wr: WR,
170}
171
172impl<BUS, DC, WR> ParallelInterface<BUS, DC, WR>
173where
174    BUS: OutputBus,
175    // The Eq bound is used by the `set_value` optimization in the generic bus
176    BUS::Word: From<u8> + Eq + core::ops::BitXor<Output = BUS::Word>,
177    DC: OutputPin,
178    WR: OutputPin,
179{
180    /// Create new parallel GPIO interface for communication with a display driver
181    pub fn new(bus: BUS, dc: DC, wr: WR) -> Self {
182        Self { bus, dc, wr }
183    }
184
185    /// Consume the display interface and return
186    /// the bus and GPIO pins used by it
187    pub fn release(self) -> (BUS, DC, WR) {
188        (self.bus, self.dc, self.wr)
189    }
190
191    /// Sends a single word to the display.
192    fn send_word(
193        &mut self,
194        word: BUS::Word,
195    ) -> Result<(), ParallelError<BUS::Error, DC::Error, WR::Error>> {
196        self.wr.set_low().map_err(ParallelError::Wr)?;
197        self.bus.set_value(word).map_err(ParallelError::Bus)?;
198        self.wr.set_high().map_err(ParallelError::Wr)
199    }
200}
201
202impl<BUS, DC, WR> Interface for ParallelInterface<BUS, DC, WR>
203where
204    BUS: OutputBus,
205    // The Eq bound is used by the `set_value` optimization in the generic bus.
206    // The BitXor is also needed for that optimization.
207    BUS::Word: From<u8> + Eq + core::ops::BitXor<Output = BUS::Word>,
208    DC: OutputPin,
209    WR: OutputPin,
210{
211    type Word = BUS::Word;
212    type Error = ParallelError<BUS::Error, DC::Error, WR::Error>;
213
214    const KIND: InterfaceKind = BUS::KIND;
215
216    async fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> {
217        // Set DC pin low for command
218        self.dc.set_low().map_err(ParallelError::Dc)?;
219        self.send_word(BUS::Word::from(command))?;
220
221        // Set DC pin high for data
222        self.dc.set_high().map_err(ParallelError::Dc)?;
223        for &arg in args {
224            self.send_word(BUS::Word::from(arg))?;
225        }
226
227        Ok(())
228    }
229
230    async fn send_data_slice(&mut self, data: &[Self::Word]) -> Result<(), Self::Error> {
231        // DC pin is expected to be high (data mode) from a previous command.
232        // We just need to send the words.
233        for &word in data {
234            self.send_word(word)?;
235        }
236        Ok(())
237    }
238}