FT81x

A minimal framebuffer driver for EVE FT81x smart display controllers. Works for BT81x too.

Single-buffered partial render mode supported for now. It may not support DSPI or QSPI currently.

Tested at 32 MHz on ft812 and at 23 MHz on bt817.

Usage

#define FB_SIZE             800 * 2 * 50
#define MAX_TRANSFER_SIZE   FB_SIZE

static void spi_cb(lv_display_t * disp, lv_ft81x_spi_operation operation, void * data, uint32_t length)
{
    spi_device_handle_t spi = lv_ft81x_get_user_data(disp);
    switch(operation) {
        case LV_FT81X_SPI_OPERATION_CS_ASSERT:
            gpio_set_level(CS_PIN, 0);
            break;
        case LV_FT81X_SPI_OPERATION_CS_DEASSERT:
            gpio_set_level(CS_PIN, 1);
            esp_rom_delay_us(10); /* tiny delay in case a CS_ASSERT immediately follows */
            break;
        case LV_FT81X_SPI_OPERATION_SEND: {
            spi_transaction_t trans = {0};
            while(length) {
                uint32_t sz = length < MAX_TRANSFER_SIZE ? length : MAX_TRANSFER_SIZE;
                trans.length = sz * 8;
                trans.rxlength = 0;
                trans.tx_buffer = data;
                spi_device_polling_transmit(spi, &trans);
                length -= sz;
                data += sz;
            }
            break;
        }
        case LV_FT81X_SPI_OPERATION_RECEIVE: {
            spi_transaction_t trans = {0};
            trans.length = length * 8;
            trans.rxlength = length * 8;
            trans.rx_buffer = data;
            spi_device_polling_transmit(spi, &trans);
            break;
        }
    }
}

int main() {
    /* ... */

    // reset the ft81x
    gpio_set_level(PD_PIN, 0);
    vTaskDelay(6 / portTICK_PERIOD_MS);
    gpio_set_level(PD_PIN, 1);
    vTaskDelay(21 / portTICK_PERIOD_MS);

    /* ... */

    // taken from https://github.com/lvgl/lvgl_esp32_drivers/blob/9fed1cc47b5a45fec6bae08b55d2147d3b50260c/lvgl_tft/EVE_config.h
    // NHD-5.0-800480FT-CxXx-xxx 800x480 5.0" Newhaven, resistive or capacitive, FT81x
    // EVE_NHD_50
    #define EVE_VSYNC0      (0L)
    #define EVE_VSYNC1      (3L)
    #define EVE_VOFFSET     (32L)
    #define EVE_VCYCLE      (525L)
    #define EVE_HSYNC0      (0L)
    #define EVE_HSYNC1      (48L)
    #define EVE_HOFFSET     (88L)
    #define EVE_HCYCLE      (928L)
    #define EVE_PCLKPOL     (0L)
    #define EVE_SWIZZLE     (0L)
    #define EVE_PCLK        (2L)
    #define EVE_CSPREAD     (1L)

    lv_ft81x_parameters_t params = {
        .hor_res = 800,
        .ver_res = 480,

        .hcycle = EVE_HCYCLE,
        .hoffset = EVE_HOFFSET,
        .hsync0 = EVE_HSYNC0,
        .hsync1 = EVE_HSYNC1,
        .vcycle = EVE_VCYCLE,
        .voffset = EVE_VOFFSET,
        .vsync0 = EVE_VSYNC0,
        .vsync1 = EVE_VSYNC1,
        .swizzle = EVE_SWIZZLE,
        .pclkpol = EVE_PCLKPOL,
        .cspread = EVE_CSPREAD,
        .pclk = EVE_PCLK,

        .has_crystal = true,
        .is_bt81x = false
    };

    static uint8_t fb[FB_SIZE] __attribute__((aligned(4)));
    lv_display_t * disp = lv_ft81x_create(&params, fb, FB_SIZE, spi_cb, spi);

    /* ... */
}

Troubleshooting

If the backlight does not come on (or is too bright or dim), try changing the value of PWM_DUTY_BACKLIGHT_ON in lv_ft81x.c, which can vary by board.