lcd_async/interface/
spi.rs

1//!
2//! Async SPI interface for MIPI DCS displays.
3//!
4//! This module provides an async implementation of the [`Interface`] trait for SPI-based TFT displays.
5//! It is designed for use with async runtimes and drivers, and does not require an internal buffer—
6//! pixel data is sent directly from the provided slice.
7//!
8//! # Example
9//!
10//! ```rust,ignore
11//! use lcd_async::interface::SpiInterface;
12//! use embedded_hal_async::spi::SpiDevice;
13//! use embedded_hal::digital::OutputPin;
14//!
15//! let spi = /* your async SPI device */;
16//! let dc = /* your DC OutputPin */;
17//! let mut iface = SpiInterface::new(spi, dc);
18//! // Use iface with the display driver
19//! ```
20
21use embedded_hal::digital::OutputPin;
22use embedded_hal_async::spi::SpiDevice;
23
24use super::{Interface, InterfaceKind};
25
26/// Error type for the async SPI interface.
27///
28/// Wraps errors from the SPI bus or the data/command (DC) pin.
29#[derive(Clone, Copy, Debug)]
30pub enum SpiError<SPI, DC> {
31    /// SPI bus error
32    Spi(SPI),
33    /// Data/command pin error
34    Dc(DC),
35}
36
37/// Async SPI interface for MIPI DCS displays.
38///
39/// This struct implements the [`Interface`] trait for SPI-based displays, using an async [`SpiDevice`]
40/// and a data/command (DC) output pin. Unlike the original mipidsi version, this async variant does not
41/// use an internal buffer; all pixel data is sent directly from the provided slice.
42///
43/// Use [`SpiInterface::new`] to construct, and [`SpiInterface::release`] to deconstruct and recover the SPI and DC resources.
44pub struct SpiInterface<SPI, DC> {
45    spi: SPI,
46    dc: DC,
47}
48
49impl<SPI, DC> SpiInterface<SPI, DC>
50where
51    SPI: SpiDevice,
52    DC: OutputPin,
53{
54    /// Create a new async SPI interface from an SPI device and DC pin.
55    pub fn new(spi: SPI, dc: DC) -> Self {
56        Self { spi, dc }
57    }
58
59    /// Release the DC pin and SPI peripheral back, deconstructing the interface.
60    pub fn release(self) -> (SPI, DC) {
61        (self.spi, self.dc)
62    }
63}
64
65impl<SPI, DC> Interface for SpiInterface<SPI, DC>
66where
67    SPI: SpiDevice, // Assuming async
68    DC: OutputPin,  // Ensure OutputPin methods are compatible with your async context
69{
70    type Word = u8; // For SPI, Word is u8. send_data_slice will take &[u8]
71    type Error = SpiError<SPI::Error, DC::Error>;
72
73    const KIND: InterfaceKind = InterfaceKind::Serial4Line;
74
75    /// Send a command and its arguments to the display controller.
76    ///
77    /// The DC pin is set low for the command byte, then high for the argument bytes.
78    async fn send_command(&mut self, command: u8, args: &[u8]) -> Result<(), Self::Error> {
79        self.dc.set_low().map_err(SpiError::Dc)?;
80        self.spi.write(&[command]).await.map_err(SpiError::Spi)?;
81        self.dc.set_high().map_err(SpiError::Dc)?;
82        self.spi.write(args).await.map_err(SpiError::Spi)?;
83        Ok(())
84    }
85
86    /// Send a slice of pixel or data bytes to the display controller.
87    ///
88    /// The data is sent as-is over SPI, with the DC pin assumed to be high.
89    async fn send_data_slice(&mut self, data: &[Self::Word]) -> Result<(), Self::Error> {
90        // data is &[u8] because Self::Word = u8
91        // Directly send the user's framebuffer slice.
92        // The underlying SPI driver might do its own buffering/chunking if necessary.
93        self.spi.write(data).await.map_err(SpiError::Spi)?;
94        Ok(())
95    }
96}