mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-10 04:37:55 +08:00
feat(drivers): add ft81x framebuffer driver (#7815)
This commit is contained in:
@@ -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(¶ms, 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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*/
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
**********************/
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user