feat(drivers): add ft81x framebuffer driver (#7815)

This commit is contained in:
Liam Howatt
2025-04-28 21:25:56 +02:00
committed by GitHub
parent 56fbfabc79
commit 00cd8e766d
10 changed files with 1489 additions and 0 deletions
+4
View File
@@ -2018,6 +2018,10 @@ menu "LVGL configuration"
default n
depends on LV_USE_ST_LTDC && !LV_USE_DRAW_DMA2D
config LV_USE_FT81X
bool "Use ft81x EVE driver"
default n
config LV_USE_WINDOWS
bool "Use LVGL Windows backend"
depends on LV_OS_WINDOWS
@@ -0,0 +1,114 @@
=====
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
*****
.. code-block:: c
#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.
+1
View File
@@ -1254,6 +1254,7 @@
#define LV_USE_ST7789 0
#define LV_USE_ST7796 0
#define LV_USE_ILI9341 0
#define LV_USE_FT81X 0
#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#define LV_USE_GENERIC_MIPI 1
+399
View File
@@ -0,0 +1,399 @@
/**
* @file lv_ft81x.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_ft81x.h"
#if LV_USE_FT81X
#include "lv_ft81x_defines.h"
#include "../../../stdlib/lv_mem.h"
#include "../../../stdlib/lv_sprintf.h"
#include "../../../stdlib/lv_string.h"
#include "../../../misc/lv_types.h"
#include "../../../misc/lv_utils.h"
/*********************
* DEFINES
*********************/
/* Increase as functionality is added if needed. */
#define LV_FT81X_CMD_BUF_SIZE 63
/* The PWM value that corresponds to the backlight being "on".
0x80 was found to work on at least two boards but should
be changed as needed. */
#define PWM_DUTY_BACKLIGHT_ON 0x80
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_ft81x_spi_cb_t spi_cb;
void * user_data;
uint16_t cmd_offset;
} lv_ft81x_driver_data_t;
typedef struct {
uint32_t buf_len;
uint8_t buf[LV_FT81X_CMD_BUF_SIZE];
} lv_ft81x_cmd_list_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t initialize(lv_display_t * disp, const lv_ft81x_parameters_t * params);
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
static void delete_cb(lv_event_t * e);
static void lv_ft81x_cmd(lv_display_t * disp, uint8_t command, uint8_t parameter);
static uint8_t lv_ft81x_read_8(lv_display_t * disp, uint32_t address);
static uint16_t lv_ft81x_read_16(lv_display_t * disp, uint32_t address);
/* static uint32_t lv_ft81x_read_32(lv_display_t * disp, uint32_t address); */
static void lv_ft81x_write_8(lv_display_t * disp, uint32_t address, uint8_t val);
static void lv_ft81x_write_16(lv_display_t * disp, uint32_t address, uint16_t val);
static void lv_ft81x_write_32(lv_display_t * disp, uint32_t address, uint32_t val);
static void lv_ft81x_cmd_list_init(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list);
static void lv_ft81x_cmd_list_add_16(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list, uint16_t value);
static void lv_ft81x_cmd_list_add_32(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list, uint32_t value);
static void lv_ft81x_cmd_list_send(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list);
static void lv_ft81x_encode_read_address(void * dst_4_bytes, uint32_t address);
static void lv_ft81x_encode_write_address(void * dst_3_bytes, uint32_t address);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_BIG_ENDIAN_SYSTEM
#define BE_TO_OR_FROM_NATIVE_32(x) ((uint32_t)(x))
#define BE_TO_OR_FROM_NATIVE_16(x) ((uint16_t)(x))
#define LE_TO_OR_FROM_NATIVE_32(x) lv_swap_bytes_32(x)
#define LE_TO_OR_FROM_NATIVE_16(x) lv_swap_bytes_16(x)
#else
#define BE_TO_OR_FROM_NATIVE_32(x) lv_swap_bytes_32(x)
#define BE_TO_OR_FROM_NATIVE_16(x) lv_swap_bytes_16(x)
#define LE_TO_OR_FROM_NATIVE_32(x) ((uint32_t)(x))
#define LE_TO_OR_FROM_NATIVE_16(x) ((uint16_t)(x))
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_display_t * lv_ft81x_create(const lv_ft81x_parameters_t * params, void * partial_buf, uint32_t buf_size,
lv_ft81x_spi_cb_t spi_cb, void * user_data)
{
LV_ASSERT_NULL(spi_cb);
lv_display_t * disp = lv_display_create(params->hor_res, params->ver_res);
lv_ft81x_driver_data_t * drv = lv_malloc_zeroed(sizeof(lv_ft81x_driver_data_t));
LV_ASSERT_MALLOC(drv);
drv->spi_cb = spi_cb;
drv->user_data = user_data;
drv->cmd_offset = 0;
lv_display_set_driver_data(disp, drv);
lv_display_set_flush_cb(disp, flush_cb);
lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
lv_display_set_buffers(disp, partial_buf, NULL, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_add_event_cb(disp, delete_cb, LV_EVENT_DELETE, NULL);
lv_result_t init_res = initialize(disp, params);
if(init_res != LV_RESULT_OK) {
lv_display_delete(disp);
return NULL;
}
return disp;
}
void * lv_ft81x_get_user_data(lv_display_t * disp)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
return drv->user_data;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t initialize(lv_display_t * disp, const lv_ft81x_parameters_t * params)
{
lv_ft81x_cmd(disp, params->has_crystal ? EVE_CLKEXT : EVE_CLKINT, 0);
if(params->is_bt81x) lv_ft81x_cmd(disp, EVE_CLKSEL, 0x46);
lv_ft81x_cmd(disp, EVE_ACTIVE, 0);
/* at least 40 ms is needed for EVE to become ready. */
lv_delay_ms(40);
uint32_t start_millis = lv_tick_get();
while(lv_ft81x_read_8(disp, REG_ID) != 0x7c) {
if(lv_tick_get() - start_millis > 1000) {
LV_LOG_ERROR("failed to read 0x7C from the ID register 1000 ms after activation.");
return LV_RESULT_INVALID;
}
lv_delay_ms(1);
};
start_millis = lv_tick_get();
while(lv_ft81x_read_8(disp, REG_CPURESET) & 0x03) {
if(lv_tick_get() - start_millis > 1000) {
LV_LOG_ERROR("CPURESET register coprocessor and touch engines not in \"working status\" 1000 ms after activation.");
return LV_RESULT_INVALID;
}
lv_delay_ms(1);
};
if(params->is_bt81x) lv_ft81x_write_32(disp, REG_FREQUENCY, 72000000);
lv_ft81x_write_8(disp, REG_PWM_DUTY, PWM_DUTY_BACKLIGHT_ON);
lv_ft81x_write_16(disp, REG_HSIZE, params->hor_res); /* active display width */
lv_ft81x_write_16(disp, REG_HCYCLE, params->hcycle); /* total number of clocks per line, incl front/back porch */
lv_ft81x_write_16(disp, REG_HOFFSET, params->hoffset); /* start of active line */
lv_ft81x_write_16(disp, REG_HSYNC0, params->hsync0); /* start of horizontal sync pulse */
lv_ft81x_write_16(disp, REG_HSYNC1, params->hsync1); /* end of horizontal sync pulse */
lv_ft81x_write_16(disp, REG_VSIZE, params->ver_res); /* active display height */
lv_ft81x_write_16(disp, REG_VCYCLE, params->vcycle); /* total number of lines per screen, including pre/post */
lv_ft81x_write_16(disp, REG_VOFFSET, params->voffset); /* start of active screen */
lv_ft81x_write_16(disp, REG_VSYNC0, params->vsync0); /* start of vertical sync pulse */
lv_ft81x_write_16(disp, REG_VSYNC1, params->vsync1); /* end of vertical sync pulse */
lv_ft81x_write_8(disp, REG_SWIZZLE, params->swizzle); /* FT8xx output to LCD - pin order */
lv_ft81x_write_8(disp, REG_PCLK_POL, params->pclkpol); /* LCD data is clocked in on this PCLK edge */
lv_ft81x_write_8(disp, REG_CSPREAD,
params->cspread); /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */
/* write a basic display-list to get things started */
lv_ft81x_write_32(disp, EVE_RAM_DL, DL_CLEAR_RGB);
lv_ft81x_write_32(disp, EVE_RAM_DL + 4, (DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG));
lv_ft81x_write_32(disp, EVE_RAM_DL + 8, DL_DISPLAY); /* end of display list */
lv_ft81x_write_32(disp, REG_DLSWAP, EVE_DLSWAP_FRAME);
/* nothing is being displayed yet... the pixel clock is still 0x00 */
lv_ft81x_write_8(disp, REG_GPIO,
0x80); /* enable the DISP signal to the LCD panel, it is set to output in REG_GPIO_DIR by default */
lv_ft81x_write_8(disp, REG_PCLK, params->pclk); /* now start clocking data to the LCD panel */
LV_ASSERT(lv_ft81x_read_16(disp, REG_CMD_READ) != 0xfff);
LV_ASSERT(0 == lv_ft81x_read_16(disp, REG_CMD_WRITE));
lv_ft81x_cmd_list_t cmd_list;
lv_ft81x_cmd_list_init(disp, &cmd_list);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, CMD_MEMSET);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, 0); /* address */
lv_ft81x_cmd_list_add_32(disp, &cmd_list, 0x00); /* val */
lv_ft81x_cmd_list_add_32(disp, &cmd_list, 2 * params->hor_res * params->ver_res); /* count */
lv_ft81x_cmd_list_send(disp, &cmd_list);
lv_ft81x_cmd_list_init(disp, &cmd_list);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, CMD_DLSTART);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, DL_CLEAR_RGB | 0); /* clear to black */
lv_ft81x_cmd_list_add_32(disp, &cmd_list, DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, TAG(0));
lv_ft81x_cmd_list_add_32(disp, &cmd_list, TAG(20));
lv_ft81x_cmd_list_add_32(disp, &cmd_list, CMD_SETBITMAP);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, 0); /* address */
lv_ft81x_cmd_list_add_16(disp, &cmd_list, EVE_RGB565);
lv_ft81x_cmd_list_add_16(disp, &cmd_list, params->hor_res);
lv_ft81x_cmd_list_add_16(disp, &cmd_list, params->ver_res);
lv_ft81x_cmd_list_add_16(disp, &cmd_list, 0);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, DL_BEGIN | EVE_BITMAPS);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, VERTEX2F(0, 0));
lv_ft81x_cmd_list_add_32(disp, &cmd_list, DL_END);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, TAG(0));
lv_ft81x_cmd_list_add_32(disp, &cmd_list, DL_DISPLAY);
lv_ft81x_cmd_list_add_32(disp, &cmd_list, CMD_SWAP);
lv_ft81x_cmd_list_send(disp, &cmd_list);
return LV_RESULT_OK;
}
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
if(drv->spi_cb == NULL) {
LV_LOG_ERROR("The SPI callback is NULL.");
return;
}
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
uint32_t disp_row_bytes = hor_res * 2;
uint32_t address = disp_row_bytes * area->y1 + area->x1 * 2;
uint8_t encoded_address[3];
if(lv_area_get_width(area) == hor_res) {
lv_ft81x_encode_write_address(encoded_address, address);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, encoded_address, sizeof(encoded_address));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, px_map, lv_area_get_size(area) * 2);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
}
else {
uint32_t area_height = lv_area_get_height(area);
uint32_t area_row_bytes = lv_area_get_width(area) * 2;
for(uint32_t i = 0; i < area_height; i++) {
lv_ft81x_encode_write_address(encoded_address, address);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, encoded_address, sizeof(encoded_address));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, px_map, area_row_bytes);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
address += disp_row_bytes;
px_map += area_row_bytes;
}
}
lv_display_flush_ready(disp);
}
static void delete_cb(lv_event_t * e)
{
lv_display_t * disp = lv_event_get_target(e);
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
lv_free(drv);
}
static void lv_ft81x_cmd(lv_display_t * disp, uint8_t command, uint8_t parameter)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
uint8_t data[3] = {command, parameter, 0x00};
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, data, sizeof(data));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
}
static uint8_t lv_ft81x_read_8(lv_display_t * disp, uint32_t address)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
lv_ft81x_encode_read_address(&address, address);
uint8_t val;
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, &address, sizeof(address));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_RECEIVE, &val, sizeof(val));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
return val;
}
static uint16_t lv_ft81x_read_16(lv_display_t * disp, uint32_t address)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
lv_ft81x_encode_read_address(&address, address);
uint16_t val;
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, &address, sizeof(address));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_RECEIVE, &val, sizeof(val));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
val = LE_TO_OR_FROM_NATIVE_16(val);
return val;
}
static void lv_ft81x_write_8(lv_display_t * disp, uint32_t address, uint8_t val)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
uint8_t data[4];
lv_ft81x_encode_write_address(data, address);
data[3] = val;
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, data, sizeof(data));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
}
static void lv_ft81x_write_16(lv_display_t * disp, uint32_t address, uint16_t val)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
val = LE_TO_OR_FROM_NATIVE_16(val);
uint8_t data[5];
lv_ft81x_encode_write_address(data, address);
lv_memcpy(data + 3, &val, 2);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, data, sizeof(data));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
}
static void lv_ft81x_write_32(lv_display_t * disp, uint32_t address, uint32_t val)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
val = LE_TO_OR_FROM_NATIVE_32(val);
uint8_t data[7];
lv_ft81x_encode_write_address(data, address);
lv_memcpy(data + 3, &val, 4);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, data, sizeof(data));
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
}
static void lv_ft81x_cmd_list_init(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
LV_ASSERT_MSG(LV_FT81X_CMD_BUF_SIZE >= 3, "increase LV_FT81X_CMD_BUF_SIZE as needed");
lv_ft81x_encode_write_address(cmd_list->buf, EVE_RAM_CMD + drv->cmd_offset);
cmd_list->buf_len = 3;
}
static void lv_ft81x_cmd_list_add_16(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list, uint16_t value)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
uint8_t * buf_dst = &cmd_list->buf[cmd_list->buf_len];
cmd_list->buf_len += 2;
LV_ASSERT_MSG(cmd_list->buf_len <= LV_FT81X_CMD_BUF_SIZE, "increase LV_FT81X_CMD_BUF_SIZE as needed");
value = LE_TO_OR_FROM_NATIVE_16(value);
lv_memcpy(buf_dst, &value, 2);
drv->cmd_offset = (drv->cmd_offset + 2) & 0xfff; /* circular */
}
static void lv_ft81x_cmd_list_add_32(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list, uint32_t value)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
uint8_t * buf_dst = &cmd_list->buf[cmd_list->buf_len];
cmd_list->buf_len += 4;
LV_ASSERT_MSG(cmd_list->buf_len <= LV_FT81X_CMD_BUF_SIZE, "increase LV_FT81X_CMD_BUF_SIZE as needed");
value = LE_TO_OR_FROM_NATIVE_32(value);
lv_memcpy(buf_dst, &value, 4);
drv->cmd_offset = (drv->cmd_offset + 4) & 0xfff; /* circular */
}
static void lv_ft81x_cmd_list_send(lv_display_t * disp, lv_ft81x_cmd_list_t * cmd_list)
{
lv_ft81x_driver_data_t * drv = lv_display_get_driver_data(disp);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_ASSERT, NULL, 0);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_SEND, cmd_list->buf, cmd_list->buf_len);
drv->spi_cb(disp, LV_FT81X_SPI_OPERATION_CS_DEASSERT, NULL, 0);
lv_ft81x_write_16(disp, REG_CMD_WRITE, drv->cmd_offset);
}
static void lv_ft81x_encode_read_address(void * dst_4_bytes, uint32_t address)
{
address = BE_TO_OR_FROM_NATIVE_32(address << 8);
lv_memcpy(dst_4_bytes, &address, 4);
}
static void lv_ft81x_encode_write_address(void * dst_3_bytes, uint32_t address)
{
address = BE_TO_OR_FROM_NATIVE_32((address | 0x800000) << 8);
lv_memcpy(dst_3_bytes, &address, 3);
}
#endif /*LV_USE_FT81X*/
+93
View File
@@ -0,0 +1,93 @@
/**
* @file lv_ft81x.h
*
*/
#ifndef LV_FT81X_H
#define LV_FT81X_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_FT81X
#include "../../../display/lv_display.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t hor_res;
uint16_t ver_res;
uint16_t hcycle;
uint16_t hoffset;
uint16_t hsync0;
uint16_t hsync1;
uint16_t vcycle;
uint16_t voffset;
uint16_t vsync0;
uint16_t vsync1;
uint8_t swizzle;
uint8_t pclkpol;
uint8_t cspread;
uint8_t pclk;
bool has_crystal;
bool is_bt81x;
} lv_ft81x_parameters_t;
typedef enum {
LV_FT81X_SPI_OPERATION_CS_ASSERT,
LV_FT81X_SPI_OPERATION_CS_DEASSERT,
LV_FT81X_SPI_OPERATION_SEND,
LV_FT81X_SPI_OPERATION_RECEIVE
} lv_ft81x_spi_operation;
typedef void (*lv_ft81x_spi_cb_t)(lv_display_t * disp, lv_ft81x_spi_operation operation, void * data, uint32_t length);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a framebuffer-based ft81x driver display.
* @param params pointer to a struct of display panel properties. does not need to be static.
* @param partial_buf a single partial buffer
* @param buf_size size of the partial buffer
* @param spi_cb a callback called by the driver to perform SPI operations
* @param user_data use `lv_ft81x_get_user_data` to get this pointer inside the SPI callback
* @return pointer to the display
*/
lv_display_t * lv_ft81x_create(const lv_ft81x_parameters_t * params, void * partial_buf, uint32_t buf_size,
lv_ft81x_spi_cb_t spi_cb, void * user_data);
/**
* Get the `user_data` parameter that was passed to `lv_ft81x_create`. Useful in the SPI callback.
* @param disp pointer to the ft81x display
* @return the `user_data` pointer
*/
void * lv_ft81x_get_user_data(lv_display_t * disp);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FT81X*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FT81X_H*/
File diff suppressed because it is too large Load Diff
+1
View File
@@ -33,6 +33,7 @@ extern "C" {
#include "display/renesas_glcdc/lv_renesas_glcdc.h"
#include "display/st_ltdc/lv_st_ltdc.h"
#include "display/ft81x/lv_ft81x.h"
#include "nuttx/lv_nuttx_entry.h"
#include "nuttx/lv_nuttx_fbdev.h"
+7
View File
@@ -4049,6 +4049,13 @@
#define LV_USE_ILI9341 0
#endif
#endif
#ifndef LV_USE_FT81X
#ifdef CONFIG_LV_USE_FT81X
#define LV_USE_FT81X CONFIG_LV_USE_FT81X
#else
#define LV_USE_FT81X 0
#endif
#endif
#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#ifndef LV_USE_GENERIC_MIPI
+23
View File
@@ -57,6 +57,29 @@ void * lv_utils_bsearch(const void * key, const void * base, size_t n, size_t si
*/
lv_result_t lv_draw_buf_save_to_file(const lv_draw_buf_t * draw_buf, const char * path);
/**
* Reverse the order of the bytes in a 32-bit value.
* @param x a 32-bit value.
* @return the value `x` with reversed byte-order.
*/
static inline uint32_t lv_swap_bytes_32(uint32_t x)
{
return (x << 24)
| ((x & 0x0000ff00U) << 8)
| ((x & 0x00ff0000U) >> 8)
| (x >> 24);
}
/**
* Reverse the order of the bytes in a 16-bit value.
* @param x a 16-bit value.
* @return the value `x` with reversed byte-order.
*/
static inline uint16_t lv_swap_bytes_16(uint16_t x)
{
return (x << 8) | (x >> 8);
}
/**********************
* MACROS
**********************/
+1
View File
@@ -154,6 +154,7 @@
#define LV_USE_ST7735 1
#define LV_USE_ST7789 1
#define LV_USE_ST7796 1
#define LV_USE_FT81X 1
#ifndef LV_USE_LIBINPUT
#define LV_USE_LIBINPUT 1