therealprof / display-interface

Rust crates providing a generic interface for display drivers and some default implementations (GPIO, SPI and I2C)
Apache License 2.0
73 stars 27 forks source link

16 bit bus or padding for command parameters #46

Open bdolgov opened 8 months ago

bdolgov commented 8 months ago

Hello!

I have a problem with ILI9488 similar to the one described in #35. I am using ILI9488 through waveshare's board: https://www.waveshare.com/wiki/Pico-ResTouch-LCD-3.5.

For some reason (probably to increase frequency?), this board doesn't connect the controller to the SPI interface of ILI9488 directly. Instead it uses the parallel interface of ILI9488 and has its own circuit to translate from SPI to parallel: LCD SPI ->16BIT. This circuit is always 16 bits: it reads 16 bits from SPI, and sets D0..D15 of ILI9488 to the provided values.

Command parameters in ILI9488 are still 8 bit and are set on D0..D7. It means that to send command parameters through this SPI to parallel converter, they need to be padded with zeroes: send_data(U8([a, b, c, d])) needs to write [0, a, 0, b, 0, c, 0, d] to the SPI bus. But this transformation does not apply to the pixel data: as ILI9488 reads pixels from D0..D15, send_data(U8([a, b, c, d])) needs to write [a, b, c, d] to the SPI bus.

Because the WriteOnlyDataCommand trait does not differentiate between parameters and pixels in send_data, I can't see an easy way to support my display without changing display interface or drives. I see the following options:

  1. Extend WriteOnlyDataCommand with the send_parameters function. It's default implementation would just call send_data, so it should not be a huge compatibility problem. Slowly migrate drivers to use send_parameters instead of send_data for parameters.

    If this happens, I will be able to implement my own interface that does padding for commands and not for pixels.

  2. Use DataFormat to decide on the padding: when the input data is U8, pad it; if the input data is U16, don't pad it.

    It will work, for example, with mipidsi, because it always sends command parameters as U8, and allows me to plug a custom function that will use U16 for pixel data. But do other drivers do the same? I can imagine some drivers using U16 to send 16-bit parameters, like SetColumnAddress start and end, or even mipidsi doing this in the future. (ccing @almindor as FYI in case you also have an opinion on this).

  3. Declare that this weird board is not display-interface's problem: the driver should be aware about the board circuitry, and pad the parameters with zeroes before calling write_data.

  4. Make a statful display interface implementation that remembers the last command that was sent. Pad data if the last command has parameters, and not pad the data if the last command starts the transfer of pixels.

What option looks the best moving forward?

almindor commented 8 months ago

I suspect this will have to fall on the driver side, but it poses a good general question on "format mapping and representation" in general. I don't have a strong opinion either way here though.

GrantM11235 commented 3 months ago

Can you just use the parallel PGpio16BitInterface with a custom OutputBus?

pub struct SpiToParallel<Spi>(Spi);

impl<Spi: SpiDevice> OutputBus for SpiToParallel<Spi> {
    type Word = u16;

    fn set_value(&mut self, value: Self::Word) -> Result<(), DisplayError> {
        let value = value.to_be_bytes(); // <- Or maybe to_le_bytes()
        self.0.write(&value).map_err(|_| DisplayError::BusWriteError)
    }
}