Setting the endianness for the SPI bus in the esp_lcd component (IDFGH-11659)
Is your feature request related to a problem?
It kind of is related to a problem. On some displays only when transferring to the display using SPI the byte order for each color needs to be reversed. Typically when data is written to the frame buffer it is written as {r, g, b, r, g, b, r, g, b} But how it needs to be sent over the SPI bus is {b, g, r, b, g, r} When using 24 bit color the problem is easily solved by setting the color mode to BGR but when using RGB565 you cannot do that because the 2 bytes that represent the RGB need to stay as RGB and the 2 bytes get flip flopped.
Describe the solution you'd like.
The current implimentation of the esp-lcd component for the SPI bus allow for changing the bit order but not the byte order. There are displays that require reversing the byte order of the data. when using 24 bit color this is less of an issue because the color mode can be set to BGR which reverses the byte order. But when using RGB565 that cannot be done. the first and second bytes have to keep the bits where they are and the 2 bytes need to be flip flopped.
I believe this would be better to handle at the time the data is being sent to the bus. This is because the frame buffer is already being iterated over in order to send the data it would simple be a matter of altering that iteration so it could candle the byte swapping It would be less overhead then having to iterate over the frame buffer moving bytes around and then passing the frame buffer off to the bus only to have the buffer iterated over a second time to be sent.
Describe alternatives you've considered.
No response
Additional context.
No response
AFAIK, some SPI LCD supports to swap the bytes order. in this https://github.com/espressif/esp-idf/commit/6f55f7634446ef2759af2835a7a636c8491e39e1 commit, we introduced a new config entry: esp_lcd_panel_dev_config_t::data_endian
That's good that it has been added but I think it is added to the wrong place. This is a bus thing and not a display thing. I am only utilizing the bus portion of the API and not the panel portion. This is because the bus drivers I am writing are for MicroPython and the panels need to be written in Python code not in C code.
C = compile time Python = runtime
Want to have the availability of changing the display that is attached to the MCU without the need to compile and flash the ESP.
I am not seeing how that field ends up getting used to swap the byte order around for SPI. Do you know where that is done? Is it done by the driver via a C function that is written into the driver. That is what I don't want to do. Having to iterate over the buffer once to change the byte order and then a second time when sending is more overhead and it will slow down the display updates.
To keep the performance up I would imagine the best place for the byte order to be changed is when the buffer is being sent.
What you are asking is not related to SPI probably. It's about how data are stored in memory. SPI transfer data from memory in which data are in little endian byte order. Yes SPI have bit-order about how byte is shifted out in HW.
When sending the display data to the display and if the display is set to RGB565 the data needs to be sent as follows..
And RGB565 pixel value is arranged as such. When dealing with a raw RGB frame buffer the data is stored as you see below into that buffer.
r7, r6, r5, r4, r3 g7, g6, g5, g4 | g3, g2, b7, b6, b5, b4, b3
When transferring the data to a display the display wants the bytes sent to it this way..
g3, g2, b7, b6, b5, b4, b3 | r7, r6, r5, r4, r3 g7, g6, g5, g4
I don't think the order in which the color is stored in the GRAM makes any difference with how the bytes gets sent for RGB565. The reason why I say this is because when sending RGB888 no reordering of the bytes has to take place prior to sending the data. The reordering of the bytes take a lot of time to do and if it was possible to do it as it was being transferred it would be better because the entire buffer would not have to be iterated over to swap the bytes around for each pixel.
I don't know why a display would want to see the byte endian change per pixel and only for RGB565 and not RGB888.
OK so I have been doing some investigation into this and this is what I have come up with..
in the esp_lcd component there is a whole lot of disorder with respect to setting the byte order.
I want us to work with the same verbiage so we don't get all kinds of confused.
-
color byte order: This is the order of the RGB values. Using "byte" is technically an erroneous thing to do because a color is not always made of of 3 bytes. We are going to stick with calling it this as the term byte is to infer "R", "G" and "B" as being one byte each. Color byte order can be RGB, BGR, GRB BRG, etc....
-
color bit order: This infers the order in which each byte in a color has it's bits ordered. You have most significant and least significant. if you are using RGB888 this would be the order of the bits for each of the 8 bytes. If you are using RGB565 there are only 2 bytes and this would be the order of the bits in those 3 bytes.
-
color endianness: This is the byte order of the color at the actual byte level. This gets mixed up with the color byte order most commonly. The reason why is if you have the endian as MSB RGB888 gets transmitted as RGB and if you set it to little endian RGB888 gets sent as BGR. The same thing is achievable by setting the color byte order to BGR but that only holds true for RGB888 and sometimes RGB666.... with RGB565 it's a different story. This is because 1/2 of the green bits are the bottom 3 bits of byte 1 and the last 3 bits of green are the top 3 bits of byte 2. if you change the endian to LSB what happens is the bottom 3 bits of the green are what gets transmitted first then the 5 bits for blue then the top 3 bits of green get sent and lastly the 5 bits for red,.
ok so now that we have a common vocabulary here are the issues....
In esp_lcd component there are a few "bus" types that are used to pass data to a display.
MIPI DBI type A (I6800): not supported by the esp_lcd component. MIPI DBI type B (I8080): supported MIPI DBI type C (SPI): supported MIPI DSI: supported
I8080 (MIPI DBI type B) interface:
reverse_color_bits
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_io_i80.h#L82
swap_color_bytes
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_io_i80.h#L83
SPI (MIPI DBI type C) interface:
lsb_first
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_io_spi.h#L41
DSI (MIPI DSI) interface:
pixel_format
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h#L86
and then we have these to use in the panel drivers.
data_endian
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_panel_dev.h#L27
rgb_ele_order
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_panel_dev.h#L25
If you look at these 2 you can see where there is going to be some confusion as to what the first one really is.
lcd_rgb_element_order_t
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_types.h#L41
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/include/esp_lcd_types.h#L46
There is a very inconsistent way things are named. and I want to point out specifically this..
https://github.com/espressif/esp-idf/blob/46acfdce969f03c02b001fe4d24fa9e98f6adc5e/components/esp_lcd/src/esp_lcd_panel_st7789.c#L113
That is being done completely blind as to what kind of a bus is being used and it is not caring what has been set for that bus either...
Here is the datasheet for the ST7789 IC.
https://www.waveshare.com/w/upload/a/ae/ST7789_Datasheet.pdf#page=258
If you read this page about 1/2 way down you will see this statement...
Note: Little Endian only can be supported in 65K 8-bit and 9-bit interface.
This display IC supports RGB, SPI and I8080 interfaces. But that specific setting is only going to work on an I8080 8 or 9 lane connection.
With respect to the I8080 interface changing the color endianness to LSB when the interface is using 16 lanes is simply a matter of reordering the pins to change the order. BUT!... Espressif has built in a way to do this at the hardware level no matter what the number of lanes are. This is done using the swap_color_bytes field in the bus config structure. now due to the way things are named there would be a whole mess of confusion as to what needs to be set. Due to the lack of any checks being made at the panel level to see what type of bus is being used and what settings have been set is going to lead to all kinds of unwanted behavior from the drivers.
There needs to be a single location where the RGB order, byte order and bit order gets set. There should not be multiple locations where things can ultimately end up bumping heads.
Most display IC's do not provide the ability to change the per pixel byte order. and with most display IC's when transmitting RGB565 pixel data the pixel bytes need to be LSB. The ability to do this at the hardware level would be ideal. If there was a way this is able to be done without having to iterate over the entire buffer to swap the 2 bytes for each pixel prior to transmitting it... That would be fantastic!!! It is able to be done with the I8080 bus driver but not for the SPI driver.