Merge branch 'feat/i80_lcd_esp32p4' into 'master'

Add i80 LCD support on ESP32P4

See merge request espressif/esp-idf!32829
This commit is contained in:
morris 2024-08-21 18:04:39 +08:00
commit fdd90d4894
35 changed files with 315 additions and 366 deletions

View File

@ -26,13 +26,17 @@ if(CONFIG_SOC_GPSPI_SUPPORTED)
list(APPEND srcs "spi/esp_lcd_panel_io_spi.c")
endif()
if(CONFIG_SOC_I2S_LCD_I80_VARIANT)
if(CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA)
list(APPEND srcs "i80/esp_lcd_panel_io_i2s.c")
endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED)
if(CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED)
list(APPEND srcs "i80/esp_lcd_panel_io_i80.c")
endif()
if(CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED)
list(APPEND includes "rgb/include")
list(APPEND srcs "i80/esp_lcd_panel_io_i80.c" "rgb/esp_lcd_panel_rgb.c")
list(APPEND srcs "rgb/esp_lcd_panel_rgb.c")
endif()
if(CONFIG_SOC_MIPI_DSI_SUPPORTED)

View File

@ -2,14 +2,6 @@ menu "LCD and Touch Panel"
comment "LCD Touch Drivers are maintained in the ESP Component Registry"
menu "LCD Peripheral Configuration"
config LCD_PANEL_IO_FORMAT_BUF_SIZE
depends on SOC_LCD_I80_SUPPORTED
int "LCD panel io format buffer size"
default 32
help
LCD driver allocates an internal buffer to transform the data into a proper format, because of
the endian order mismatch. This option is to set the size of the buffer, in bytes.
config LCD_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n

View File

@ -161,7 +161,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
bus->format_buffer = heap_caps_calloc(1, max_transfer_bytes, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
#else
// only transform format for LCD parameters, buffer size depends on specific LCD, set at compile time
bus->format_buffer = heap_caps_calloc(1, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
#endif // SOC_I2S_TRANS_SIZE_ALIGN_WORD
ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer");
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
@ -186,7 +186,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// install interrupt service, (I2S LCD mode only uses the "TX Unit", which leaves "RX Unit" for other purpose)
// So the interrupt should also be able to share with other functionality
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus->bus_id].irq_id, isr_flags,
ret = esp_intr_alloc_intrstatus(lcd_periph_i2s_signals.buses[bus->bus_id].irq_id, isr_flags,
(uint32_t)i2s_ll_get_intr_status_reg(bus->hal.dev),
I2S_LL_EVENT_TX_EOF, lcd_default_isr_handler, bus, &bus->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
@ -514,7 +514,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_panel_io_i80_t *cur_device = bus->cur_device;
lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(param_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes");
assert(param_size <= CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE && "format buffer too small, increase CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE");
assert(param_size <= LCD_I80_IO_FORMAT_BUF_SIZE && "format buffer too small, increase LCD_I80_IO_FORMAT_BUF_SIZE");
size_t num_trans_inflight = next_device->num_trans_inflight;
// before issue a polling transaction, need to wait queued transactions finished
for (size_t i = 0; i < num_trans_inflight; i++) {
@ -700,15 +700,15 @@ static esp_err_t i2s_lcd_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_
for (size_t i = 0; i < bus_config->bus_width; i++) {
gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
#if SOC_I2S_TRANS_SIZE_ALIGN_WORD
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i + 8], false, false);
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i2s_signals.buses[bus_id].data_sigs[i + 8], false, false);
#else
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i + SOC_LCD_I80_BUS_WIDTH - bus_config->bus_width], false, false);
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i2s_signals.buses[bus_id].data_sigs[i + SOC_I2S_MAX_DATA_WIDTH - bus_config->bus_width], false, false);
#endif
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
}
// WR signal (pclk)
gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_signals.buses[bus_id].wr_sig, true, false);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_i2s_signals.buses[bus_id].wr_sig, true, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->wr_gpio_num], PIN_FUNC_GPIO);
// DC signal is controlled by software, set as general purpose IO
gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT);
@ -751,7 +751,7 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
}
// the WR signal (a.k.a the PCLK) generated by I2S is low level in idle stage
// but most of 8080 LCDs require the WR line to be in high level during idle stage
esp_rom_gpio_connect_out_signal(bus->wr_gpio_num, lcd_periph_signals.buses[bus->bus_id].wr_sig, !next_device->flags.pclk_idle_low, false);
esp_rom_gpio_connect_out_signal(bus->wr_gpio_num, lcd_periph_i2s_signals.buses[bus->bus_id].wr_sig, !next_device->flags.pclk_idle_low, false);
}
bus->cur_device = next_device;
}

View File

@ -28,18 +28,37 @@
#include "esp_clk_tree.h"
#include "esp_memory_utils.h"
#include "esp_cache.h"
#include "hal/dma_types.h"
#include "hal/gpio_hal.h"
#include "esp_private/gdma.h"
#include "driver/gpio.h"
#include "esp_private/gpio.h"
#include "esp_private/gdma.h"
#include "esp_private/gdma_link.h"
#include "esp_private/periph_ctrl.h"
#include "esp_lcd_common.h"
#include "soc/lcd_periph.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "hal/lcd_ll.h"
#include "hal/lcd_hal.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1))
#define ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
#define LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE 4095
#if defined(SOC_GDMA_TRIG_PERIPH_LCD0_BUS) && (SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB)
#define LCD_GDMA_NEW_CHANNEL gdma_new_ahb_channel
#define LCD_GDMA_DESCRIPTOR_ALIGN 4
#elif defined(SOC_GDMA_TRIG_PERIPH_LCD0_BUS) && (SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI)
#define LCD_GDMA_NEW_CHANNEL gdma_new_axi_channel
#define LCD_GDMA_DESCRIPTOR_ALIGN 8
#else
#error "Unsupported GDMA bus type for LCD i80"
#endif
#if SOC_NON_CACHEABLE_OFFSET
#define LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) ((addr) + SOC_NON_CACHEABLE_OFFSET)
#else
#define LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) (addr)
#endif
static const char *TAG = "lcd_panel.io.i80";
@ -66,19 +85,20 @@ struct esp_lcd_i80_bus_t {
size_t bus_width; // Number of data lines
intr_handle_t intr; // LCD peripheral interrupt handle
esp_pm_lock_handle_t pm_lock; // Power management lock
size_t num_dma_nodes; // Number of DMA descriptors
uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer
uint8_t *format_buffer_nc; // Non-cacheable version of format buffer
size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source
size_t num_dma_nodes; // Number of DMA nodes (descriptors)
gdma_channel_handle_t dma_chan; // DMA channel handle
gdma_link_list_handle_t dma_link; // DMA link list handle
size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
lcd_panel_io_i80_t *cur_device; // Current working device
LIST_HEAD(i80_device_list, lcd_panel_io_i80_t) device_list; // Head of i80 device list
struct {
unsigned int exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively
uint32_t exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively
} flags;
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
};
struct lcd_i80_trans_descriptor_t {
@ -133,20 +153,39 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// although LCD_CAM can support up to 24 data lines, we restrict users to only use 8 or 16 bit width
ESP_RETURN_ON_FALSE(bus_config->bus_width == 8 || bus_config->bus_width == 16, ESP_ERR_INVALID_ARG,
TAG, "invalid bus width:%d", bus_config->bus_width);
size_t num_dma_nodes = bus_config->max_transfer_bytes / DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1;
// DMA descriptors must be placed in internal SRAM
bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t) + num_dma_nodes * sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
// allocate i80 bus memory
bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t), LCD_I80_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus");
size_t num_dma_nodes = bus_config->max_transfer_bytes / LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1;
// create DMA link list
gdma_link_list_config_t dma_link_config = {
.buffer_alignment = 1, // no special buffer alignment for LCD TX buffer
.item_alignment = LCD_GDMA_DESCRIPTOR_ALIGN,
.num_items = num_dma_nodes,
.flags = {
.check_owner = true,
},
};
ESP_GOTO_ON_ERROR(gdma_new_link_list(&dma_link_config, &bus->dma_link), err, TAG, "create DMA link list failed");
bus->num_dma_nodes = num_dma_nodes;
bus->bus_width = bus_config->bus_width;
bus->bus_id = -1;
bus->format_buffer = heap_caps_calloc(1, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
// allocate the format buffer from internal memory, with DMA capability
bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE,
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer");
// if the buffer is behind the cache, write it back to the underlying memory
if (cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA) > 0) {
esp_cache_msync(bus->format_buffer, LCD_I80_IO_FORMAT_BUF_SIZE,
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
}
bus->format_buffer_nc = LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(bus->format_buffer);
// register to platform
int bus_id = lcd_com_register_device(LCD_COM_DEVICE_TYPE_I80, bus);
ESP_GOTO_ON_FALSE(bus_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot");
bus->bus_id = bus_id;
// enable APB to access LCD registers
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) {
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_i80_signals.buses[bus_id].module, ref_count) {
if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus_id, true);
lcd_ll_reset_register(bus_id);
@ -166,14 +205,13 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// install interrupt service, (LCD peripheral shares the same interrupt source with Camera peripheral with different mask)
// interrupt is disabled by default
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus_id].irq_id, isr_flags,
ret = esp_intr_alloc_intrstatus(lcd_periph_i80_signals.buses[bus_id].irq_id, isr_flags,
(uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev),
LCD_LL_EVENT_TRANS_DONE, lcd_default_isr_handler, bus, &bus->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts
lcd_ll_clear_interrupt_status(bus->hal.dev, UINT32_MAX); // clear pending interrupt
// install DMA service
bus->bus_width = bus_config->bus_width;
ret = lcd_i80_init_dma_link(bus, bus_config);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install DMA failed");
// disable RGB-LCD mode
@ -210,8 +248,11 @@ err:
gdma_disconnect(bus->dma_chan);
gdma_del_channel(bus->dma_chan);
}
if (bus->dma_link) {
gdma_del_link_list(bus->dma_link);
}
if (bus->bus_id >= 0) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus->bus_id].module, ref_count) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_i80_signals.buses[bus->bus_id].module, ref_count) {
if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus->bus_id, false);
}
@ -236,7 +277,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty");
int bus_id = bus->bus_id;
lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus_id);
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_i80_signals.buses[bus_id].module, ref_count) {
if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus_id, false);
}
@ -248,6 +289,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
if (bus->pm_lock) {
esp_pm_lock_delete(bus->pm_lock);
}
gdma_del_link_list(bus->dma_link);
free(bus);
ESP_LOGD(TAG, "del i80 bus(%d)", bus_id);
err:
@ -314,7 +356,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
if (io_config->cs_gpio_num >= 0) {
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high);
gpio_set_direction(io_config->cs_gpio_num, GPIO_MODE_OUTPUT);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO);
gpio_func_sel(io_config->cs_gpio_num, PIN_FUNC_GPIO);
}
*ret_io = &(i80_device->base);
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id);
@ -393,7 +435,8 @@ static uint32_t i80_lcd_prepare_param_buffer(esp_lcd_i80_bus_t *bus, lcd_panel_i
int param_per_size = i80_device->lcd_param_bits / 8;
int param_num = param_size / param_per_size;
const uint8_t *from = (const uint8_t *)lcd_param;
uint8_t *to = bus->format_buffer;
// we use non-cacheable address to write to the format buffer, for simplicity
uint8_t *to = bus->format_buffer_nc;
uint8_t step = bus->bus_width / 8;
int param_cycle = i80_device->lcd_param_bits / bus->bus_width;
// in case bus_width=16 and param_bits=8, we still need 1 param_cycle
@ -410,7 +453,7 @@ static uint32_t i80_lcd_prepare_param_buffer(esp_lcd_i80_bus_t *bus, lcd_panel_i
}
to += step;
}
return to - bus->format_buffer;
return to - bus->format_buffer_nc;
}
static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size)
@ -419,8 +462,8 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
esp_lcd_i80_bus_t *bus = next_device->bus;
lcd_panel_io_i80_t *cur_device = bus->cur_device;
lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(param_size <= (bus->num_dma_nodes * DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes");
assert(param_size <= CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE && "format buffer too small, increase CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE");
assert(param_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes");
assert(param_size <= LCD_I80_IO_FORMAT_BUF_SIZE && "format buffer too small, increase LCD_I80_IO_FORMAT_BUF_SIZE");
uint32_t cmd_cycles = next_device->lcd_cmd_bits / bus->bus_width;
// in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle
if (cmd_cycles * bus->bus_width < next_device->lcd_cmd_bits) {
@ -456,7 +499,15 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
trans_desc->data_length = trans_desc->data ? param_len : 0;
trans_desc->trans_done_cb = NULL; // no callback for parameter transaction
// mount data to DMA links
lcd_com_mount_dma_data(bus->dma_nodes, trans_desc->data, trans_desc->data_length);
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,
.mark_final = true, // singly link list, mark final descriptor
}
};
gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL);
// increase the pm lock reference count before starting a new transaction
if (bus->pm_lock) {
esp_pm_lock_acquire(bus->pm_lock);
@ -476,17 +527,22 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_panel_io_i80_t *i80_device = __containerof(io, lcd_panel_io_i80_t, base);
esp_lcd_i80_bus_t *bus = i80_device->bus;
lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(color_size <= (bus->num_dma_nodes * DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "color bytes too long, enlarge max_transfer_bytes");
assert(color_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "color bytes too long, enlarge max_transfer_bytes");
uint32_t cache_line_size = 0;
if (esp_ptr_external_ram(color)) {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
// flush frame buffer from cache to the physical PSRAM
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
} else {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
}
if (cache_line_size > 0) {
// flush data from cache to the physical memory
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
}
// in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle
@ -546,21 +602,11 @@ static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config)
{
esp_err_t ret = ESP_OK;
// chain DMA descriptors
for (int i = 0; i < bus->num_dma_nodes; i++) {
bus->dma_nodes[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU;
bus->dma_nodes[i].next = &bus->dma_nodes[i + 1];
}
bus->dma_nodes[bus->num_dma_nodes - 1].next = NULL; // one-off DMA chain
// alloc DMA channel and connect to LCD peripheral
gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
#if SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB
ret = gdma_new_ahb_channel(&dma_chan_config, &bus->dma_chan);
#elif SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI
ret = gdma_new_axi_channel(&dma_chan_config, &bus->dma_chan);
#endif
ret = LCD_GDMA_NEW_CHANNEL(&dma_chan_config, &bus->dma_chan);
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc DMA channel failed");
gdma_connect(bus->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
gdma_strategy_config_t strategy_config = {
@ -614,15 +660,15 @@ static esp_err_t lcd_i80_bus_configure_gpio(esp_lcd_i80_bus_handle_t bus, const
// connect peripheral signals via GPIO matrix
for (size_t i = 0; i < bus_config->bus_width; i++) {
gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i], false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i80_signals.buses[bus_id].data_sigs[i], false, false);
gpio_func_sel(bus_config->data_gpio_nums[i], PIN_FUNC_GPIO);
}
gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->dc_gpio_num, lcd_periph_signals.buses[bus_id].dc_sig, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->dc_gpio_num], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(bus_config->dc_gpio_num, lcd_periph_i80_signals.buses[bus_id].dc_sig, false, false);
gpio_func_sel(bus_config->dc_gpio_num, PIN_FUNC_GPIO);
gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_signals.buses[bus_id].wr_sig, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->wr_gpio_num], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_i80_signals.buses[bus_id].wr_sig, false, false);
gpio_func_sel(bus_config->wr_gpio_num, PIN_FUNC_GPIO);
return ESP_OK;
}
@ -649,8 +695,11 @@ static void lcd_start_transaction(esp_lcd_i80_bus_t *bus, lcd_i80_trans_descript
lcd_ll_set_phase_cycles(bus->hal.dev, cmd_cycles, dummy_cycles, data_cycles);
lcd_ll_set_blank_cycles(bus->hal.dev, 1, 1);
// reset FIFO before starting a new transaction
lcd_ll_fifo_reset(bus->hal.dev);
if (trans_desc->data) { // some specific LCD commands can have no parameters
gdma_start(bus->dma_chan, (intptr_t)(bus->dma_nodes));
gdma_start(bus->dma_chan, gdma_link_get_head_addr(bus->dma_link));
// delay 1us is sufficient for DMA to pass data to LCD FIFO
// in fact, this is only needed when LCD pixel clock is set too high
esp_rom_delay_us(1);
@ -676,7 +725,7 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
}
if (next_device->cs_gpio_num >= 0) {
// connect CS signal to the new device
esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_signals.buses[bus->bus_id].cs_sig,
esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_i80_signals.buses[bus->bus_id].cs_sig,
next_device->flags.cs_active_high, false);
}
}
@ -738,7 +787,15 @@ IRAM_ATTR static void lcd_default_isr_handler(void *args)
bus->cur_trans = trans_desc;
bus->cur_device = next_device;
// mount data to DMA links
lcd_com_mount_dma_data(bus->dma_nodes, trans_desc->data, trans_desc->data_length);
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,
.mark_final = true, // singly link list, mark final descriptor
}
};
gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL);
// enable interrupt again, because the new transaction can trigger new trans done event
esp_intr_enable(bus->intr);
// increase the pm lock reference count before starting a new transaction

View File

@ -19,6 +19,9 @@
extern "C" {
#endif
// size of the internal buffer to transform the data into a proper format (e.g. data endian)
#define LCD_I80_IO_FORMAT_BUF_SIZE 32
#define LCD_I80_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#define LCD_I80_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT

View File

@ -196,7 +196,7 @@ static esp_err_t lcd_rgb_panel_destroy(esp_rgb_panel_t *rgb_panel)
lcd_ll_enable_clock(rgb_panel->hal.dev, false);
}
if (rgb_panel->panel_id >= 0) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[rgb_panel->panel_id].module, ref_count) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_rgb_signals.panels[rgb_panel->panel_id].module, ref_count) {
if (ref_count == 0) {
lcd_ll_enable_bus_clock(rgb_panel->panel_id, false);
}
@ -306,7 +306,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->panel_id = panel_id;
// enable APB to access LCD registers
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[panel_id].module, ref_count) {
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_rgb_signals.panels[panel_id].module, ref_count) {
if (ref_count == 0) {
lcd_ll_enable_bus_clock(panel_id, true);
lcd_ll_reset_register(panel_id);
@ -332,7 +332,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
}
// install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask)
int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.panels[panel_id].irq_id, isr_flags,
ret = esp_intr_alloc_intrstatus(lcd_periph_rgb_signals.panels[panel_id].irq_id, isr_flags,
(uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev),
LCD_LL_EVENT_VSYNC_END, lcd_default_isr_handler, rgb_panel, &rgb_panel->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
@ -801,7 +801,7 @@ static esp_err_t rgb_panel_invert_color(esp_lcd_panel_t *panel, bool invert_colo
// inverting the data line by GPIO matrix
for (int i = 0; i < rgb_panel->data_width; i++) {
if (rgb_panel->data_gpio_nums[i] >= 0) {
esp_rom_gpio_connect_out_signal(rgb_panel->data_gpio_nums[i], lcd_periph_signals.panels[panel_id].data_sigs[i],
esp_rom_gpio_connect_out_signal(rgb_panel->data_gpio_nums[i], lcd_periph_rgb_signals.panels[panel_id].data_sigs[i],
invert_color_data, false);
}
}
@ -857,34 +857,34 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *panel, const esp_
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->data_gpio_nums[i],
lcd_periph_signals.panels[panel_id].data_sigs[i], false, false);
lcd_periph_rgb_signals.panels[panel_id].data_sigs[i], false, false);
}
}
if (panel_config->hsync_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->hsync_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->hsync_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->hsync_gpio_num,
lcd_periph_signals.panels[panel_id].hsync_sig, false, false);
lcd_periph_rgb_signals.panels[panel_id].hsync_sig, false, false);
}
if (panel_config->vsync_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->vsync_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->vsync_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->vsync_gpio_num,
lcd_periph_signals.panels[panel_id].vsync_sig, false, false);
lcd_periph_rgb_signals.panels[panel_id].vsync_sig, false, false);
}
// PCLK may not be necessary in some cases (i.e. VGA output)
if (panel_config->pclk_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->pclk_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->pclk_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->pclk_gpio_num,
lcd_periph_signals.panels[panel_id].pclk_sig, false, false);
lcd_periph_rgb_signals.panels[panel_id].pclk_sig, false, false);
}
// DE signal might not be necessary for some RGB LCD
if (panel_config->de_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->de_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->de_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->de_gpio_num,
lcd_periph_signals.panels[panel_id].de_sig, false, false);
lcd_periph_rgb_signals.panels[panel_id].de_sig, false, false);
}
// disp enable GPIO is optional
if (panel_config->disp_gpio_num >= 0) {

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -14,8 +14,8 @@
typedef struct esp_lcd_platform_t {
portMUX_TYPE spinlock; // spinlock used to protect platform level resources
union {
void *panels[SOC_LCD_RGB_PANELS]; // array of RGB LCD panel instances
void *buses[SOC_LCD_I80_BUSES]; // array of i80 bus instances
void *panels[SOC_LCDCAM_RGB_NUM_PANELS]; // array of RGB LCD panel instances
void *buses[SOC_LCDCAM_I80_NUM_BUSES]; // array of i80 bus instances
}; // LCD peripheral can only work under either RGB mode or intel 8080 mode
} esp_lcd_platform_t;
@ -30,7 +30,7 @@ int lcd_com_register_device(lcd_com_device_type_t device_type, void *device_obj)
switch (device_type) {
case LCD_COM_DEVICE_TYPE_I80:
// search for a bus slot then register to platform
for (int i = 0; (i < SOC_LCD_I80_BUSES) && (member_id == -1); i++) {
for (int i = 0; (i < SOC_LCDCAM_I80_NUM_BUSES) && (member_id == -1); i++) {
portENTER_CRITICAL(&s_lcd_platform.spinlock);
if (!s_lcd_platform.buses[i]) {
s_lcd_platform.buses[i] = device_obj;
@ -41,7 +41,7 @@ int lcd_com_register_device(lcd_com_device_type_t device_type, void *device_obj)
break;
case LCD_COM_DEVICE_TYPE_RGB:
// search for a panel slot then register to platform
for (int i = 0; (i < SOC_LCD_RGB_PANELS) && (member_id == -1); i++) {
for (int i = 0; (i < SOC_LCDCAM_RGB_NUM_PANELS) && (member_id == -1); i++) {
portENTER_CRITICAL(&s_lcd_platform.spinlock);
if (!s_lcd_platform.panels[i]) {
s_lcd_platform.panels[i] = device_obj;

View File

@ -1,4 +1,4 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
This test app is used to test LCDs with intel 8080 interface.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,7 +12,7 @@ extern "C" {
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#if CONFIG_IDF_TARGET_ESP32S3
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO (1)
#define TEST_LCD_RST_GPIO (2)
#define TEST_LCD_CS_GPIO (3)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
@ -18,7 +18,7 @@
#include "driver/gpio.h"
#include "test_i80_board.h"
#if SOC_I2S_LCD_I80_VARIANT
#if SOC_I2S_SUPPORTS_LCD_CAMERA
#include "driver/i2s_std.h"
TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
@ -49,9 +49,9 @@ TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
}
#endif // SOC_I2S_LCD_I80_VARIANT
#endif // SOC_I2S_SUPPORTS_LCD_CAMERA
#if SOC_LCDCAM_SUPPORTED
#if SOC_LCDCAM_I80_LCD_SUPPORTED
TEST_CASE("lcd_i80_device_swap_color_bytes", "[lcd]")
{
esp_lcd_i80_bus_handle_t i80_bus = NULL;
@ -173,11 +173,16 @@ TEST_CASE("lcd_i80_device_clock_mode", "[lcd]")
}
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
}
#endif // SOC_LCDCAM_SUPPORTED
#endif // SOC_LCDCAM_I80_LCD_SUPPORTED
TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
{
esp_lcd_i80_bus_handle_t i80_buses[SOC_LCD_I80_BUSES] = {};
#if SOC_I2S_SUPPORTS_LCD_CAMERA
#define TEST_NUM_LCD_I80_BUSES SOC_LCD_I80_BUSES
#elif SOC_LCDCAM_I80_LCD_SUPPORTED
#define TEST_NUM_LCD_I80_BUSES SOC_LCDCAM_I80_NUM_BUSES
#endif
esp_lcd_i80_bus_handle_t i80_buses[TEST_NUM_LCD_I80_BUSES] = {};
esp_lcd_i80_bus_config_t bus_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO,
.wr_gpio_num = TEST_LCD_PCLK_GPIO,
@ -195,7 +200,7 @@ TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
.bus_width = 8,
.max_transfer_bytes = TEST_LCD_H_RES * 40 * sizeof(uint16_t)
};
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
for (int i = 0; i < TEST_NUM_LCD_I80_BUSES; i++) {
TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_buses[i]));
}
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, esp_lcd_new_i80_bus(&bus_config, &i80_buses[0]));
@ -215,7 +220,7 @@ TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
for (int i = 0; i < 10; i++) {
TEST_ESP_OK(esp_lcd_panel_io_del(io_handles[i]));
}
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
for (int i = 0; i < TEST_NUM_LCD_I80_BUSES; i++) {
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_buses[i]));
}
}
@ -455,7 +460,7 @@ TEST_CASE("lcd_panel_with_i80_interface_(st7789, 8bits)", "[lcd]")
}
// TODO: support the test on I2S LCD (IDF-7202)
#if !SOC_I2S_LCD_I80_VARIANT
#if !SOC_I2S_SUPPORTS_LCD_CAMERA
TEST_CASE("i80_lcd_send_colors_to_fixed_region", "[lcd]")
{
int x_start = 100;

View File

@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@ -8,6 +7,7 @@ from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.generic
@pytest.mark.parametrize(
'config',

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -507,7 +507,8 @@ static inline void lcd_ll_reverse_wire_bit_order(lcd_cam_dev_t *dev, bool en)
* @brief Whether to swap adjacent two bytes
*
* @note This acts before the YUV-RGB converter, mainly to change the data endian.
* {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
* e.g. {B1,B0},{B3,B2} => {B0,B1}{B2,B3}.
* Only valid when `lcd_ll_set_dma_read_stride` set the DMA read stride >= 16 bits
*
* @param dev LCD register base address
* @param en True to swap the byte order, False to not swap
@ -606,7 +607,7 @@ static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, u
}
/**
* @brief Wether to enable RGB interface
* @brief Whether to enable RGB interface
*
* @param dev LCD register base address
* @param en True to enable RGB interface, False to disable RGB interface
@ -629,7 +630,7 @@ static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
}
/**
* @brief Wether to output HSYNC signal in porch resion
* @brief Whether to output HSYNC signal in porch resion
*
* @param dev LCD register base address
* @param en True to enable, False to disable
@ -746,7 +747,7 @@ static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
* @brief Clear interrupt status by mask
*
* @param dev LCD register base address
* @param mask Interupt status mask
* @param mask Interrupt status mask
*/
__attribute__((always_inline))
static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)

View File

@ -14,14 +14,14 @@
extern "C" {
#endif
#if SOC_LCD_I80_SUPPORTED || SOC_LCD_RGB_SUPPORTED
#if SOC_I2S_SUPPORTS_LCD_CAMERA || SOC_LCDCAM_SUPPORTED
/**
* @brief LCD clock source
*/
typedef soc_periph_lcd_clk_src_t lcd_clock_source_t;
#else
typedef int lcd_clock_source_t;
#endif // SOC_LCD_I80_SUPPORTED || SOC_LCD_RGB_SUPPORTED
#endif // SOC_I2S_SUPPORTS_LCD_CAMERA || SOC_LCDCAM_SUPPORTED
/**
* @brief RGB data endian

View File

@ -89,7 +89,7 @@ if(CONFIG_SOC_GPTIMER_SUPPORTED)
list(APPEND srcs "${target_folder}/timer_periph.c")
endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED OR CONFIG_SOC_LCD_I80_SUPPORTED)
if(CONFIG_SOC_LCDCAM_SUPPORTED OR CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA)
list(APPEND srcs "${target_folder}/lcd_periph.c")
endif()

View File

@ -443,6 +443,10 @@ config SOC_I2S_SUPPORTS_LCD_CAMERA
bool
default y
config SOC_I2S_MAX_DATA_WIDTH
int
default 24
config SOC_I2S_TRANS_SIZE_ALIGN_WORD
bool
default y

View File

@ -225,6 +225,7 @@
#define SOC_I2S_SUPPORTS_ADC (1)
#define SOC_I2S_SUPPORTS_DAC (1)
#define SOC_I2S_SUPPORTS_LCD_CAMERA (1)
#define SOC_I2S_MAX_DATA_WIDTH (24)
#define SOC_I2S_TRANS_SIZE_ALIGN_WORD (1) // I2S DMA transfer size must be aligned to word
#define SOC_I2S_LCD_I80_VARIANT (1) // I2S has a special LCD mode that can generate Intel 8080 TX timing

View File

@ -8,7 +8,7 @@
#include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = {
const lcd_i2s_signal_conn_t lcd_periph_i2s_signals = {
.buses = {
[0] = {
.module = PERIPH_I2S0_MODULE,

View File

@ -47,10 +47,18 @@ config SOC_PCNT_SUPPORTED
bool
default y
config SOC_LCDCAM_SUPPORTED
bool
default y
config SOC_LCDCAM_CAM_SUPPORTED
bool
default y
config SOC_LCDCAM_I80_LCD_SUPPORTED
bool
default y
config SOC_MIPI_CSI_SUPPORTED
bool
default y
@ -1071,26 +1079,22 @@ config SOC_RMT_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_LCD_I80_BUSES
int
default 1
config SOC_LCD_RGB_PANELS
int
default 1
config SOC_LCD_I80_BUS_WIDTH
int
default 24
config SOC_LCD_RGB_DATA_WIDTH
int
default 24
config SOC_LCD_SUPPORT_RGB_YUV_CONV
config SOC_LCD_I80_SUPPORTED
bool
default y
config SOC_LCDCAM_I80_NUM_BUSES
int
default 1
config SOC_LCDCAM_I80_BUS_WIDTH
int
default 24
config SOC_LCDCAM_RGB_NUM_PANELS
int
default 1
config SOC_MCPWM_GROUPS
int
default 2

View File

@ -28,8 +28,10 @@
#define SOC_DMA2D_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_PCNT_SUPPORTED 1
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465
#define SOC_LCDCAM_SUPPORTED 1
#define SOC_LCDCAM_CAM_SUPPORTED 1
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1 // support the Intel 8080 bus driver based on the LCD_CAM peripheral
// #define SOC_LCDCAM_RGB_LCD_SUPPORTED 1 // TODO: IDF-7465
#define SOC_MIPI_CSI_SUPPORTED 1
#define SOC_MIPI_DSI_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1
@ -404,14 +406,13 @@
#define SOC_RMT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up RMT registers before sleep */
/*-------------------------- LCD CAPS ----------------------------------------*/
/* I80 bus and RGB timing generator can't work at the same time */
// #define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */ // TODO: IDF-7465
// #define SOC_LCD_RGB_SUPPORTED (1) /*!< RGB LCD is supported */ // TODO: IDF-7465
#define SOC_LCD_I80_BUSES (1U) /*!< Has one LCD Intel 8080 bus */
#define SOC_LCD_RGB_PANELS (1U) /*!< Support one RGB LCD panel */
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
#define SOC_LCD_RGB_DATA_WIDTH (24) /*!< Number of LCD data lines */
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
/* I80 bus and RGB timing generator can't work at the same time in the LCD_CAM peripheral */
#define SOC_LCD_I80_SUPPORTED 1 /*!< support intel 8080 driver */
#define SOC_LCDCAM_I80_NUM_BUSES 1U /*!< LCD_CAM peripheral provides one LCD Intel 8080 bus */
#define SOC_LCDCAM_I80_BUS_WIDTH 24 /*!< Intel 8080 bus max data width */
#define SOC_LCDCAM_RGB_NUM_PANELS 1U /*!< Support one RGB LCD panel */
// #define SOC_LCD_RGB_DATA_WIDTH 24 /*!< Number of LCD data lines */
// #define SOC_LCD_SUPPORT_RGB_YUV_CONV 1 /*!< Support color format conversion between RGB and YUV */
/*-------------------------- MCPWM CAPS --------------------------------------*/
#define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,7 +7,8 @@
#include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = {
#if SOC_LCDCAM_I80_LCD_SUPPORTED
const lcd_i80_signal_conn_t lcd_periph_i80_signals = {
.buses = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,
@ -42,7 +43,12 @@ const lcd_signal_conn_t lcd_periph_signals = {
.dc_sig = LCD_DC_PAD_OUT_IDX,
.wr_sig = LCD_PCLK_PAD_OUT_IDX
}
},
}
};
#endif // SOC_LCDCAM_I80_LCD_SUPPORTED
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
const lcd_rgb_signal_conn_t lcd_periph_rgb_signals = {
.panels = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,
@ -80,3 +86,4 @@ const lcd_signal_conn_t lcd_periph_signals = {
}
}
};
#endif // SOC_LCDCAM_RGB_LCD_SUPPORTED

View File

@ -487,6 +487,10 @@ config SOC_I2S_SUPPORTS_LCD_CAMERA
bool
default y
config SOC_I2S_MAX_DATA_WIDTH
int
default 24
config SOC_I2S_APLL_MIN_FREQ
int
default 250000000

View File

@ -216,6 +216,7 @@
#define SOC_I2S_SUPPORTS_PLL_F160M (1)
#define SOC_I2S_SUPPORTS_DMA_EQUAL (1)
#define SOC_I2S_SUPPORTS_LCD_CAMERA (1)
#define SOC_I2S_MAX_DATA_WIDTH (24)
#define SOC_I2S_APLL_MIN_FREQ (250000000)
#define SOC_I2S_APLL_MAX_FREQ (500000000)
#define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware

View File

@ -7,7 +7,7 @@
#include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = {
const lcd_i2s_signal_conn_t lcd_periph_i2s_signals = {
.buses = {
[0] = {
.module = PERIPH_I2S0_MODULE,

View File

@ -63,6 +63,14 @@ config SOC_LCDCAM_SUPPORTED
bool
default y
config SOC_LCDCAM_I80_LCD_SUPPORTED
bool
default y
config SOC_LCDCAM_RGB_LCD_SUPPORTED
bool
default y
config SOC_MCPWM_SUPPORTED
bool
default y
@ -767,6 +775,22 @@ config SOC_LCD_SUPPORT_RGB_YUV_CONV
bool
default y
config SOC_LCDCAM_I80_NUM_BUSES
int
default 1
config SOC_LCDCAM_I80_BUS_WIDTH
int
default 16
config SOC_LCDCAM_RGB_NUM_PANELS
int
default 1
config SOC_LCDCAM_RGB_DATA_WIDTH
int
default 16
config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
int
default 128

View File

@ -32,6 +32,8 @@
#define SOC_AHB_GDMA_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_LCDCAM_SUPPORTED 1
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1
#define SOC_LCDCAM_RGB_LCD_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1
#define SOC_DEDICATED_GPIO_SUPPORTED 1
#define SOC_CACHE_SUPPORT_WRAP 1
@ -291,6 +293,10 @@
#define SOC_LCD_I80_BUS_WIDTH (16) /*!< Intel 8080 bus width */
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
#define SOC_LCDCAM_I80_NUM_BUSES (1U) /*!< LCD_CAM peripheral provides one LCD Intel 8080 bus */
#define SOC_LCDCAM_I80_BUS_WIDTH (16) /*!< Intel 8080 bus max data width */
#define SOC_LCDCAM_RGB_NUM_PANELS (1U) /*!< LCD_CAM peripheral provides one RGB panel */
#define SOC_LCDCAM_RGB_DATA_WIDTH (16) /*!< RGB panel max data width */
/*-------------------------- RTC CAPS --------------------------------------*/
#define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,7 +7,7 @@
#include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = {
const lcd_i80_signal_conn_t lcd_periph_i80_signals = {
.buses = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,
@ -34,7 +34,10 @@ const lcd_signal_conn_t lcd_periph_signals = {
.dc_sig = LCD_DC_IDX,
.wr_sig = LCD_PCLK_IDX
}
},
}
};
const lcd_rgb_signal_conn_t lcd_periph_rgb_signals = {
.panels = {
[0] = {
.module = PERIPH_LCD_CAM_MODULE,

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,42 +13,49 @@
extern "C" {
#endif
#if SOC_LCDCAM_SUPPORTED
#if SOC_LCDCAM_I80_LCD_SUPPORTED
typedef struct {
struct {
const periph_module_t module;
const int irq_id;
const int data_sigs[SOC_LCD_I80_BUS_WIDTH];
const int data_sigs[SOC_LCDCAM_I80_BUS_WIDTH];
const int cs_sig;
const int dc_sig;
const int wr_sig;
} buses[SOC_LCD_I80_BUSES];
} buses[SOC_LCDCAM_I80_NUM_BUSES];
} lcd_i80_signal_conn_t;
extern const lcd_i80_signal_conn_t lcd_periph_i80_signals;
#endif // SOC_LCDCAM_I80_LCD_SUPPORTED
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
typedef struct {
struct {
const periph_module_t module;
const int irq_id;
const int data_sigs[SOC_LCD_RGB_DATA_WIDTH];
const int data_sigs[SOC_LCDCAM_RGB_DATA_WIDTH];
const int hsync_sig;
const int vsync_sig;
const int pclk_sig;
const int de_sig;
} panels[SOC_LCD_RGB_PANELS];
} lcd_signal_conn_t;
} panels[SOC_LCDCAM_RGB_NUM_PANELS];
} lcd_rgb_signal_conn_t;
extern const lcd_signal_conn_t lcd_periph_signals;
#endif // SOC_LCDCAM_SUPPORTED
extern const lcd_rgb_signal_conn_t lcd_periph_rgb_signals;
#endif // SOC_LCDCAM_RGB_LCD_SUPPORTED
#if SOC_I2S_LCD_I80_VARIANT
#if SOC_I2S_SUPPORTS_LCD_CAMERA
typedef struct {
struct {
const periph_module_t module;
const int irq_id;
const int data_sigs[SOC_LCD_I80_BUS_WIDTH];
const int data_sigs[SOC_I2S_MAX_DATA_WIDTH];
const int wr_sig;
} buses[SOC_LCD_I80_BUSES];
} lcd_signal_conn_t;
} lcd_i2s_signal_conn_t;
extern const lcd_signal_conn_t lcd_periph_signals;
#endif // SOC_I2S_LCD_I80_VARIANT
extern const lcd_i2s_signal_conn_t lcd_periph_i2s_signals;
#endif // SOC_I2S_SUPPORTS_LCD_CAMERA
#ifdef __cplusplus
}

View File

@ -1,7 +1,7 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
# LVGL porting example (based on i80 interfaced LCD controller)
# i80 LCD LVGL porting example
LVGL is an open-source graphics library for creating modern GUIs. It has plenty of built-in graphical elements with low memory footprint, which is friendly for embedded GUI applications.
@ -29,34 +29,25 @@ The connection between ESP Board and the LCD is as follows:
```text
ESP Board LCD Screen
┌─────────────┐ ┌────────────────┐
│ │ │ │
│ 3V3 ├─────────────►│ VCC │
│ │ │ │
│ GND ├──────────────┤ GND │
│ │ │ │
│ DATA[0..7] │◄────────────►│ DATA[0..7] │
│ │ │ │
│ PCLK ├─────────────►│ PCLK │
│ │ │ │
│ CS ├─────────────►│ CS │
│ │ │ │
│ D/C ├─────────────►│ D/C │
│ │ │ │
│ RST ├─────────────►│ RST │
│ │ │ │
│ BK_LIGHT ├─────────────►│ BCKL │
│ │ │ │
│ │ └────────────────┘
│ │ LCD TOUCH
│ │ ┌────────────────┐
│ │ │ │
│ I2C SCL ├─────────────►│ I2C SCL │
│ │ │ │
│ I2C SDA │◄────────────►│ I2C SDA │
│ │ │ │
└─────────────┘ └────────────────┘
+-------------+ +----------------+
| | | |
| 3V3 +------------->| VCC |
| | | |
| GND +--------------+ GND |
| | | |
| DATA[0..7] |<------------>| DATA[0..7] |
| | | |
| PCLK +------------->| PCLK |
| | | |
| CS +------------->| CS |
| | | |
| D/C +------------->| D/C |
| | | |
| RST +------------->| RST |
| | | |
| BK_LIGHT +------------->| BCKL |
| | | |
+-------------+ +----------------+
```
The GPIO number used by this example can be changed in [i80_controller_example_main.c](main/i80_controller_example_main.c).
@ -69,9 +60,8 @@ Run `idf.py set-target <target-name>` to select one supported target that can ru
Run `idf.py menuconfig` to open a terminal UI where you can tune specific configuration for this example in the `Example Configuration` menu.
* `i80 LCD controller model`: Choose the LCD model to use by the example. If you choose `NT35510`, there will be another relevant configuration `NT35510 Data Width`, to choose the data line width for your NT35510 LCD module.
* `Allocate color data from PSRAM`: Select this option if you want to allocate the LVGL draw buffers from PSRAM.
* `Pixel clock frequency (Hz)`: Set the pixel clock frequency for the LCD controller.
* `LCD image source from`: Select where to load the image resource. See [Image Resource](#image-resource) for more details.
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A fancy animation will show up on the LCD as expected.
@ -99,16 +89,6 @@ I (558) example: Starting LVGL task
I (638) example: Display LVGL animation
```
## Touch Screen Support
This example supports touch screen connected via I2C. You can enable it by running `idf.py menuconfig` and navigating to `Example Configuration -> Enable LCD touch`. When touch is enabled, there will be a new button in the GUI that can restart the animation.
These touch controllers are supported:
* [GT911](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_gt911)
* [TT21100](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_tt21100)
* [FT5X06](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_ft5x06)
## Image Resource
This example supports two ways of reading images
@ -120,6 +100,6 @@ This example supports two ways of reading images
* Can't get a stable UI when `EXAMPLE_LCD_I80_COLOR_IN_PSRAM` is enabled.
This is because of the limited PSRAM bandwidth, compared to the internal SRAM. You can either decrease the PCLK clock `EXAMPLE_LCD_PIXEL_CLOCK_HZ` in [i80_controller_example_main.c](main/i80_controller_example_main.c) or increase the PSRAM working frequency `SPIRAM_SPEED` from the KConfig (e.g. **ESP32S3-Specific** -> **Set RAM clock speed**) or decrease the FPS in LVGL configuration. For illustration, this example has set the refresh period to 100ms in the default sdkconfig file.
This is because of the limited PSRAM bandwidth, compared to the internal SRAM. You can either decrease the PCLK clock `EXAMPLE_LCD_PIXEL_CLOCK_HZ` from the menuconfig or increase the PSRAM working frequency `SPIRAM_SPEED` from the KConfig (e.g. **ESP32S3-Specific** -> **Set RAM clock speed**) or decrease the FPS in LVGL configuration. For illustration, this example has set the refresh period to 100ms in the default sdkconfig file.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -2,11 +2,17 @@ menu "Example Configuration"
config EXAMPLE_LCD_I80_COLOR_IN_PSRAM
bool "Allocate color data from PSRAM"
depends on IDF_TARGET_ESP32S3
depends on SOC_PSRAM_DMA_CAPABLE
default y
help
Enable this option if you wish to allocate the color buffer used by LVGL from PSRAM.
Unmatched PSRAM band width with LCD requirement can lead to blurred image display.
Enable this option if you want to allocate the LVGL draw buffer from PSRAM.
config EXAMPLE_LCD_PIXEL_CLOCK_HZ
int "Pixel clock frequency (Hz)"
default 2000000 if EXAMPLE_LCD_I80_COLOR_IN_PSRAM && IDF_TARGET_ESP32S3
default 10000000
help
Set the pixel clock frequency in Hz.
choice EXAMPLE_LCD_I80_CONTROLLER_MODEL
prompt "i80 LCD controller model"
@ -45,29 +51,6 @@ menu "Example Configuration"
default 16 if EXAMPLE_LCD_NT35510_DATA_WIDTH_16
default 8
config EXAMPLE_LCD_TOUCH_ENABLED
bool "Enable LCD touch"
default n
help
Enable this option if you wish to use display touch. You can select from three touch controllers.
choice EXAMPLE_LCD_TOUCH_CONTROLLER
prompt "LCD touch controller model"
depends on EXAMPLE_LCD_TOUCH_ENABLED
default EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
help
Select LCD touch controller model
config EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
bool "GT911"
config EXAMPLE_LCD_TOUCH_CONTROLLER_TT21100
bool "TT21100"
config EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
bool "FT5X06"
endchoice
choice EXAMPLE_LCD_IMAGE_SOURCE
prompt "LCD image source from"
default EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY

View File

@ -9,36 +9,21 @@
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_touch.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
#include "esp_lcd_touch_gt911.h"
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_TT21100
#include "esp_lcd_touch_tt21100.h"
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
#include "esp_lcd_touch_ft5x06.h"
#endif
static const char *TAG = "example";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
// PCLK frequency can't go too high as the limitation of PSRAM bandwidth
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (2 * 1000 * 1000)
#else
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#endif // CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ CONFIG_EXAMPLE_LCD_PIXEL_CLOCK_HZ
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
@ -81,12 +66,6 @@ static const char *TAG = "example";
#define EXAMPLE_LCD_PARAM_BITS 8
#endif
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
#define EXAMPLE_I2C_NUM 0 // I2C number
#define EXAMPLE_I2C_SCL 39
#define EXAMPLE_I2C_SDA 40
#endif
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
@ -117,29 +96,6 @@ static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static void example_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t touchpad_x[1] = {0};
uint16_t touchpad_y[1] = {0};
uint8_t touchpad_cnt = 0;
/* Read touch controller data */
esp_lcd_touch_read_data(drv->user_data);
/* Get coordinates */
bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
if (touchpad_pressed && touchpad_cnt > 0) {
data->point.x = touchpad_x[0];
data->point.y = touchpad_y[0];
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
#endif
static void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
@ -342,64 +298,6 @@ void example_init_lcd_panel(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_h
*panel = panel_handle;
}
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
void example_init_lcd_touch(esp_lcd_touch_handle_t *tp_handle)
{
esp_lcd_touch_handle_t tp = NULL;
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
const i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = EXAMPLE_I2C_SDA,
.scl_io_num = EXAMPLE_I2C_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
/* Initialize I2C */
ESP_ERROR_CHECK(i2c_param_config(EXAMPLE_I2C_NUM, &i2c_conf));
ESP_ERROR_CHECK(i2c_driver_install(EXAMPLE_I2C_NUM, i2c_conf.mode, 0, 0, 0));
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_TT21100
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
#endif
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
/* Touch IO handle */
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)EXAMPLE_I2C_NUM, &tp_io_config, &tp_io_handle));
esp_lcd_touch_config_t tp_cfg = {
.x_max = EXAMPLE_LCD_V_RES,
.y_max = EXAMPLE_LCD_H_RES,
.rst_gpio_num = -1,
.int_gpio_num = -1,
.flags = {
.swap_xy = 1,
.mirror_x = 1,
.mirror_y = 0,
},
};
/* Initialize touch */
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
ESP_LOGI(TAG, "Initialize touch controller GT911");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_TT21100
ESP_LOGI(TAG, "Initialize touch controller TT21100");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, &tp));
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
ESP_LOGI(TAG, "Initialize touch controller FT5X06");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp));
#endif
*tp_handle = tp;
}
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
void app_main(void)
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
@ -425,11 +323,6 @@ void app_main(void)
esp_lcd_panel_handle_t panel_handle = NULL;
example_init_lcd_panel(io_handle, &panel_handle);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
esp_lcd_touch_handle_t tp_handle = NULL;
example_init_lcd_touch(&tp_handle);
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
// Stub: user can flush pre-defined pattern to the screen before we turn on the screen or backlight
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
@ -474,16 +367,6 @@ void app_main(void)
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static lv_indev_drv_t indev_drv; // Input device driver (Touch)
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.disp = disp;
indev_drv.read_cb = example_lvgl_touch_cb;
indev_drv.user_data = tp_handle;
lv_indev_drv_register(&indev_drv);
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
lvgl_mux = xSemaphoreCreateRecursiveMutex();
assert(lvgl_mux);
ESP_LOGI(TAG, "Create LVGL task");

View File

@ -1,6 +1,2 @@
dependencies:
idf: ">=4.4"
lvgl/lvgl: "~8.3.0"
esp_lcd_touch_gt911: "^1.0"
esp_lcd_touch_tt21100: "^1.0"
esp_lcd_touch_ft5x06: "^1.0"
lvgl/lvgl: "~8.4.0"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
@ -24,7 +24,6 @@ typedef struct {
} my_timer_context_t;
static my_timer_context_t my_tim_ctx;
static lv_obj_t *btn;
static lv_obj_t *arc[3];
static lv_obj_t *img_logo = NULL;
static lv_obj_t *img_text = NULL;
@ -78,9 +77,6 @@ static void anim_timer_cb(lv_timer_t *timer)
// Delete timer when all animation finished
if ((count += 5) == 220) {
lv_timer_del(timer);
// Enable button
lv_obj_clear_state(btn, LV_STATE_DISABLED);
} else {
timer_ctx->count_val = count;
}
@ -118,15 +114,6 @@ static void start_animation(lv_obj_t *scr)
my_tim_ctx.count_val = -90;
my_tim_ctx.scr = scr;
lv_timer_create(anim_timer_cb, 20, &my_tim_ctx);
// Disable button
lv_obj_add_state(btn, LV_STATE_DISABLED);
}
static void btn_cb(lv_event_t *e)
{
lv_obj_t *scr = lv_event_get_user_data(e);
start_animation(scr);
}
void example_lvgl_demo_ui(lv_disp_t *disp)
@ -140,13 +127,6 @@ void example_lvgl_demo_ui(lv_disp_t *disp)
#elif CONFIG_EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY
lv_img_set_src(img_logo, &esp_logo);
#endif
btn = lv_btn_create(scr);
lv_obj_t *lbl = lv_label_create(btn);
lv_label_set_text_static(lbl, LV_SYMBOL_REFRESH" SHOW AGAIN");
lv_obj_set_style_text_font(lbl, &lv_font_montserrat_20, 0);
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 30, -30);
// Button event
lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, scr);
start_animation(scr);
}

View File

@ -5,3 +5,4 @@ CONFIG_LV_USE_USER_DATA=y
CONFIG_LV_COLOR_16_SWAP=y
CONFIG_LV_COLOR_DEPTH_16=y
CONFIG_LV_FONT_MONTSERRAT_20=y
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

View File

@ -0,0 +1,3 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_SPEED_200M=y

View File

@ -3,5 +3,4 @@ CONFIG_SPIRAM_SPEED_80M=y
# Enabling the following configurations can help increase the PCLK frequency in the case when
# the Frame Buffer is allocated from the PSRAM and fetched by EDMA
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y