EVE External GPU Renderer

EVE is a type of external GPU IC which accepts high-level drawing commands over SPI and outputs the rendered graphics to a display over parallel RGB.

The advantage of using an EVE chip is that the rendering responsibility is removed from the driving MCU so it can be a lower-spec part or dedicate more of its processing time to other tasks. The SPI interface is simpler to connect than parallel RGB especially if the EVE chip is integrated into a display assembly.

LVGL features a renderer for EVE. LVGL UIs can be rendered by EVE and are effectively indistinguishable from the software renderer is most cases.

See also the FT81x framebuffer driver. It drives the same EVE chips but is a simpler, more standalone implementation which uses software rendering and sends all the pixels over SPI so it is much slower.

Limitations

  • Image format, size, and count limit.

  • Font format, size, and count limit.

  • The total number of tasks rendered per refresh has an upper limit.

  • Layers are not supported.

Usage

Board Parameters

Find your display parameters and populate a lv_draw_eve_parameters_t struct with them.

Here is an example lv_draw_eve_target_parameters.h for the Riverdi RVT50HQBNWC00-B which has a BT817Q — the EVE chip. These parameters were taken from the collection of configs provided by the EVE library supporting this renderer. See here. Check there for your board parameters.

lv_draw_eve_parameters_t params = {
    .hor_res = 800,
    .ver_res = 480,
    .vsync0 = 0,
    .vsync1 = 4,
    .voffset = 8,
    .vcycle = 496,
    .hsync0 = 0,
    .hsync1 = 4,
    .hoffset = 8,
    .hcycle = 816,
    .pclk = 3,
    .pclkpol = 1,
    .swizzle = 0,
    .cspread = 0,
    .has_crystal = true,
    .has_gt911 = false,
    .backlight_freq = 4000,
    .backlight_pwm = 128,
};

EVE Chip IO Implementation

The user is required to implement the GPIO and SPI IO functionality. The LVGL EVE renderer will call this callback to perform SPI communication with the EVE chip.

static void op_cb(lv_display_t * disp, lv_draw_eve_operation_t operation,
                  void * data, uint32_t length)
{
    /* optional: get the `user_data` parameter you passed to `lv_draw_eve_display_create` */
    void * your_user_data = lv_draw_eve_display_get_user_data(disp);

    switch(operation) {
        case LV_DRAW_EVE_OPERATION_POWERDOWN_SET:
            /* setting the pin low powers down the EVE chip */
            your_gpio_write(PD_N_PIN, 0);
            break;
        case LV_DRAW_EVE_OPERATION_POWERDOWN_CLEAR:
            /* setting the pin high powers on the EVE chip */
            your_gpio_write(PD_N_PIN, 1);
            break;
        case LV_DRAW_EVE_OPERATION_CS_ASSERT:
            /* setting the pin low asserts the EVE chip SPI device */
            your_gpio_write(CS_N_PIN, 0);
            break;
        case LV_DRAW_EVE_OPERATION_CS_DEASSERT:
            /* setting the pin high de-asserts the EVE chip SPI device */
            your_gpio_write(CS_N_PIN, 1);
            break;
        case LV_DRAW_EVE_OPERATION_SPI_SEND:
            /* `data` is the data to send */
            your_spi_transmit(data, length);
            break;
        case LV_DRAW_EVE_OPERATION_SPI_RECEIVE:
            /* `data` is the destination for the data */
            your_spi_receive(data, length);
            break;
    }
}

You will also need to initialize your SPI peripheral and GPIO pins.

22 MHz was the highest SPI speed that worked during testing with the Riverdi board and the ESP32-S3. You may not have success with this speed so it is recommended to validate with an SPI_SPEED value of 10 (10 MHz) and increase experimentally in your testing.

LVGL EVE Display Creation

To create the LVGL display for the EVE renderer, you call lv_draw_eve_display_create(params, op_cb, your_user_data) which returns the created display. your_user_data can be NULL. It should be called after GPIO and SPI is initialized. You may choose to initialize your IO the first time op_cb is called.

No buffers are required for the LVGL EVE renderer because no pixels are written to any buffers in the device running LVGL. When something needs to be drawn, a series of commands are sent to EVE.

Touch Indev Creation

lv_draw_eve_touch_create(disp) creates a touch Input Device (lv_indev) for the display.

You may need to configure the i2c address of the touch controller connected to EVE. See the section EVE Register Access for more info about register access.

Here is an example of setting the REG_TOUCH_CONFIG register on a BT817q EVE chip for a capacitive touch screen with a controller that has the i2c address 0x15.

/*
15:   0: capacitive, 1: resistive         CAPACITIVE
14:   host mode                           NO
13:   reserved
12:   ignore short circuit protection     NO
11:   low-power mode                      NO
10-4: 7-bit i2c address                   0x15
 3:   reserved
 2:   suppress 300ms startup              NO
 1-0: 2-bit sampling clocks val           use 1 (the reset default)
*/
lv_draw_eve_memwrite16(disp, LV_EVE_REG_TOUCH_CONFIG, 0x0151);

Display Rotation

Efficient display rotation is fully supported through lv_display_set_rotation(). Touch input rotation is handled accordingly.

EVE Register Access

The functions lv_draw_eve_memread8(), lv_draw_eve_memread16(), lv_draw_eve_memread32(), lv_draw_eve_memwrite8(), lv_draw_eve_memwrite16(), are lv_draw_eve_memwrite32() available if needed. They are wrappers around EVE_memRead8, etc.

Register definitions and other EVE enumerations are available when you include lvgl.h under the prefix namespace LV_EVE_. I.e., REG_ID is available as LV_EVE_REG_ID and EVE_ROM_CHIPID is available as LV_EVE_EVE_ROM_CHIPID, etc.

Further Reading