Skip to main content

display_driver/bus/
qspi_flash.rs

1use crate::{Area, SolidColor};
2
3use super::{BusHardwareFill, BusRead, DisplayBus, DisplayError, ErrorType, Metadata};
4
5/// An adapter that bridges a standard [`DisplayBus`] to a QSPI-connected display.
6///
7/// Some displays attached via QSPI don't use standard display commands directly but instead emulate
8/// a SPI Flash memory interface. This adapter wraps an inner bus and automatically transforms
9/// standard display commands into the appropriate QSPI Flash opcodes.
10pub struct QspiFlashBus<B: DisplayBus> {
11    inner: B,
12}
13
14impl<B: DisplayBus> QspiFlashBus<B> {
15    /// Creates a new QspiFlashBus wrapper.
16    pub fn new(inner: B) -> Self {
17        Self { inner }
18    }
19
20    #[inline]
21    fn assert_cmd_len(&self, cmd: &[u8]) {
22        assert_eq!(
23            cmd.len(),
24            1,
25            "QspiFlashBus only supports single byte commands"
26        );
27    }
28
29    /// Formats the command and address for QSPI transfer.
30    /// This is used for common write commands except WRITE_RAM.
31    #[inline]
32    pub fn to_cmd_and_addr_command(&self, cmd: u8) -> [u8; 4] {
33        [0x02, 0x00, cmd, 0x00]
34    }
35
36    /// Formats the command and address for QSPI transfer.
37    /// This is used for WRITE_RAM command.
38    #[inline]
39    pub fn to_cmd_and_addr_write_ram(&self, cmd: u8) -> [u8; 4] {
40        [0x32, 0x00, cmd, 0x00]
41    }
42
43    /// Formats the command and address for QSPI transfer.
44    /// This is used for read commands.
45    #[inline]
46    pub fn to_cmd_and_addr_read(&self, cmd: u8) -> [u8; 4] {
47        [0x03, 0x00, cmd, 0x00]
48    }
49}
50
51impl<B: DisplayBus> ErrorType for QspiFlashBus<B> {
52    type Error = B::Error;
53}
54
55impl<B: DisplayBus> DisplayBus for QspiFlashBus<B> {
56    async fn write_cmd(&mut self, cmd: &[u8]) -> Result<(), Self::Error> {
57        self.assert_cmd_len(cmd);
58        let cmd = self.to_cmd_and_addr_command(cmd[0]);
59        self.inner.write_cmd(&cmd).await
60    }
61
62    async fn write_cmd_with_params(
63        &mut self,
64        cmd: &[u8],
65        params: &[u8],
66    ) -> Result<(), Self::Error> {
67        self.assert_cmd_len(cmd);
68        let cmd = self.to_cmd_and_addr_command(cmd[0]);
69        self.inner.write_cmd_with_params(&cmd, params).await
70    }
71
72    async fn write_pixels(
73        &mut self,
74        cmd: &[u8],
75        data: &[u8],
76        metadata: Metadata,
77    ) -> Result<(), DisplayError<Self::Error>> {
78        self.assert_cmd_len(cmd);
79        let cmd = self.to_cmd_and_addr_write_ram(cmd[0]);
80        self.inner.write_pixels(&cmd, data, metadata).await
81    }
82
83    fn set_reset(&mut self, reset: bool) -> Result<(), DisplayError<Self::Error>> {
84        self.inner.set_reset(reset)
85    }
86}
87
88impl<B: DisplayBus + BusHardwareFill> BusHardwareFill for QspiFlashBus<B> {
89    async fn fill_solid(
90        &mut self,
91        cmd: &[u8],
92        color: SolidColor,
93        area: Area,
94    ) -> Result<(), DisplayError<Self::Error>> {
95        self.assert_cmd_len(cmd);
96        let cmd = self.to_cmd_and_addr_write_ram(cmd[0]);
97        self.inner.fill_solid(&cmd, color, area).await
98    }
99}
100
101impl<B: DisplayBus + BusRead> BusRead for QspiFlashBus<B> {
102    async fn read_data(
103        &mut self,
104        cmd: &[u8],
105        params: &[u8],
106        buffer: &mut [u8],
107    ) -> Result<(), DisplayError<Self::Error>> {
108        self.assert_cmd_len(cmd);
109        let cmd = self.to_cmd_and_addr_read(cmd[0]);
110        self.inner.read_data(&cmd, params, buffer).await
111    }
112}