Driver for ili9488 display


I have 3.5" display with SPI interface and ili9488 driver. On first look is this chip similar to ili9341 (small differences in init sequence), but only on first look. Main difference is that this display do not support RGB565 mode over SPI. This display supports only RB666 mode (which is “stripped” RGB888 - 2 lowest bits from each color is not used), so each pixel has 3 bytes instead of 2 bytes in RGB565 mode.

So, there is my question… As a first try I used ili9341 driver from LVGL, changed init sequence and nothing happened, then I noticed problem with unsupported RGB565 mode. Solution should be change color mode from 16 bits to 24, but this is not supported in LVGL.

Another solution is rewrite each pixel before send. I identified relevant code:

def flush():

    size = (area.x2 - area.x1 + 1) * (area.y2 - area.y1 + 1)
    data_view = color_p.__dereference__(size * lv.color_t.SIZE)

    if self.end_time_ptr.int_val > self.start_time_ptr.int_val:
        self.flush_acc_setup_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val


So, I must change each pixel before sending. Do you have some tips how to do it fast and in correct way?

Thank you.

@kisvegabor / @embeddedt - does LVGL support RB666 color mode? If not, what does it take to support it?

Changing every pixel before writing it on every frame would be very slow in Micropython.
Micropython can be used in an efficient way for controlling the UI, but usually not for rendering pixels.
Even in C that would be less efficient that supporting RB666 color mode directly in LVGL.
(By the way, there is one place I’m using Micropython to convert pixels but I’m using Viper code emitter for that)

The code that you identified is only called when the ili9341 is used in non-hybrid mode.
In hybrid mode (which is the default) all flush functionality is written in C (while init and control is still written in Micropython).
Have a look at the C implementation of ili9341 flush if you plan to do any pixel-by-pixel conversion.

Anyway, I would suggest to first find out how to support RB666 color mode natively in LVGL.

@amirgon, thank you… I know about hybrid/non-hybrid mode and about C implementation of flush…

support for RGB666 has problem with byte padding and RGB888 is marked as old…

And speed… ok, I was expecting this. I was trying to write ili9341 driver in pure micropython without DMA and it was horribly slow.

It’s not supported directly. We’ve tried to avoid supporting formats thus far that don’t align nicely with standard word sizes (1 byte, 2 bytes, 4 bytes, etc.).

You’d have to translate the pixels at a bitwise level, so I’d recommend using C (or at least Viper) to do that.

Support for RGB666 is not necessary in this case, because of ili9488 uses 24 bits data transfer, where two lowest bits of each color are not used (display cannot show 24 bits colors). But in RGB666 SPI mode, you are directly transfering data in format RGB888 -> 24 bits colors.

So, there are two ways - 16bits colors to 24 bits or 32bit (ARGB) to 24 bits. First way uses less memory, but needs more calculations, second one uses more memory (2 times), but is easiest to calculate (just cut off highest byte).

Back with progress…

I made ili9488_flush function (copied modILI9341.c -> modILI9488.c, with some minor changes like init sequence - not used but… and renaming 9341 to 9488). Flush function for ILI9341 dislay contains byte swapping (RGB -> BGR), so I changed code to:

typedef struct {
    uint8_t red;
    uint8_t green;
    uint8_t blue;
} lv_color_custom_t;


STATIC void ili9488_flush(struct _disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
     /*Convert ARGB to RGB is required (cut off A-byte)*/
    uint32_t i;
    lv_color32_t* tmp32 = (lv_color32_t*) color_p;
    lv_color_custom_t* tmp24 = (lv_color_custom_t*) color_p;

    for(i=0; i < size; i++) {
            tmp24[i].red = tmp32[i];
            tmp24[i].green = tmp32[i];
            tmp24[i].blue = tmp32[i];

    ili9488_send_data(self, (void*)color_p, size * 3);

it should be faster by using something like (based on position of A-byte):

tmp24[i].full = tmp32[i].full & 0x00FFFFFF;

BUT… I am getting panic:

Guru Meditation Error: Core  1 panic'ed (StoreProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x4023260a  PS      : 0x00060031  A0      : 0x8015e9a9  A1      : 0x3ffbef50
A2      : 0x3f819ae0  A3      : 0x00060023  A4      : 0x00060021  A5      : 0x3f4544f6
A6      : 0x00000003  A7      : 0x00060023  A8      : 0x00000000  A9      : 0x00000000
A10     : 0x00000002  A11     : 0x0000586a  A12     : 0x00000001  A13     : 0x3ffc41b4
A14     : 0x00000001  A15     : 0x00000010  SAR     : 0x0000001b  EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000018  LBEG    : 0x40095e98  LEND    : 0x40095ec6  LCOUNT  : 0xffffffff
Core 1 was running in ISR context:
EPC1    : 0x4023260a  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40086f28

ELF file SHA256: 0000000000000000

Backtrace: 0x40232607:0x3ffbef50 0x4015e9a6:0x3ffbef70 0x40082a1a:0x3ffbef90 0x40082d61:0x3ffbefb0 0x4008682a:0x3ffbefe0 0x0004001e:0x00000000 |<-CORRUPTED

This looks like some memory leaks or trying to write out of reserved memory. I must dig into deep…

And yes, I forgot: - this is inspiration for my work.

As usual… root of problem was stupid.

there are two definitions of ili9341_flush. One is in modILI9341.c (for C-only driver) and second one is in espidf.c (surprisingly for hybrid driver). And guess what… Yes, I did mean that there is only one - in C-only driver. So my code used built-in-python-class flush function, which is faulty…

So, I have a functional hybrid ili9488 driver now. It uses same code like hybrid ili9341 driver, except flush function (and init sequence, of course)…

I can send it as pull request after some tests.

That’s great! Nice work!

That would be very much appreciated!

One question: are you going to change the existing ili9341 driver to support ili9488, or are you adding a new module?
If there’s a lot of common code between them, it would make sense to have a single module with two modes or inherit both modules from a common parent class, rather than duplicate the code.
Obviously the flush C function would be separate, I’m asking about the python code.

Real difference is only in flush callback and init sequence (init sequence is a list of commands, so no problem). So, it could be parent class and two display-specific.

1 Like

Here is pull request:

@amirgon @mhepp @embeddedt Hello guys, can you tell me how you can change the contrast of the display?)

Did you check the datasheet of your display? Does it support controlling the contrast?
Searching the word “contrast” on ili9341 datasheet yields nothing.

found nothing, maybe there is another option

You could do it in software by adjusting the colors yourself. It would have to either be in your styles or in the display driver.

there are ready-made functions?
if yes, you can give me a link)

You might want to look into Positive and Negative gamma control on the ILI9488,
Commands 0xE0 and 0xE1. They have many parameters and I have never modified them, but you might want to review the datahseet and see if they can benefit you somehow.

1 Like

You can use PWM to control backlight too.

Mentioned Gamma correction should help to improve colors. There are not widely usable values, it depends on display itself. But best colors for myself (and my displays) are used in my driver code (but this is very personal, I know).