mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
lcd: support I2S1 LCD mode on esp32
This commit is contained in:
parent
f35edeb5a3
commit
9422fe077a
@ -19,8 +19,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LCD_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
|
||||
#define LCD_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#define LCD_I80_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
|
||||
#define LCD_I80_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
|
||||
#define LCD_PERIPH_CLOCK_PRE_SCALE (2) // This is the minimum divider that can be applied to LCD peripheral
|
||||
|
||||
|
@ -57,7 +57,7 @@ static esp_err_t i2s_lcd_init_dma_link(esp_lcd_i80_bus_handle_t bus);
|
||||
static esp_err_t i2s_lcd_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config);
|
||||
static void i2s_lcd_trigger_quick_trans_done_event(esp_lcd_i80_bus_handle_t bus);
|
||||
static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_i80_t *next_device);
|
||||
static IRAM_ATTR void lcd_default_isr_handler(void *args);
|
||||
static void lcd_default_isr_handler(void *args);
|
||||
|
||||
struct esp_lcd_i80_bus_t {
|
||||
int bus_id; // Bus ID, index from 0
|
||||
@ -149,9 +149,16 @@ 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, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | 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");
|
||||
// I2S0 has the LCD mode, but the LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
|
||||
ESP_GOTO_ON_ERROR(i2s_priv_register_object(bus, 0), err, TAG, "register to I2S platform failed");
|
||||
bus->bus_id = 0;
|
||||
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
|
||||
int bus_id = -1;
|
||||
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
|
||||
if (i2s_priv_register_object(bus, i) == ESP_OK) {
|
||||
bus_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(bus_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot");
|
||||
bus->bus_id = bus_id;
|
||||
// initialize HAL layer
|
||||
i2s_hal_init(&bus->hal, bus->bus_id);
|
||||
// set peripheral clock resolution
|
||||
@ -163,7 +170,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
i2s_ll_tx_reset_fifo(bus->hal.dev);
|
||||
// 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 = ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED;
|
||||
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
|
||||
ret = esp_intr_alloc_intrstatus(lcd_periph_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);
|
||||
@ -257,7 +264,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
||||
uint32_t pclk_prescale = bus->resolution_hz / 2 / io_config->pclk_hz;
|
||||
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= I2S_LL_BCK_MAX_PRESCALE, ESP_ERR_NOT_SUPPORTED, err, TAG,
|
||||
"prescaler can't satisfy PCLK clock %u", io_config->pclk_hz);
|
||||
i80_device = calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t));
|
||||
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_I80_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(i80_device, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 panel io");
|
||||
// create two queues for i80 device
|
||||
i80_device->trans_queue = xQueueCreate(io_config->trans_queue_depth, sizeof(lcd_i80_trans_descriptor_t *));
|
||||
|
@ -55,7 +55,7 @@ static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
|
||||
static esp_err_t lcd_i80_bus_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config);
|
||||
static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_i80_t *next_device);
|
||||
static void lcd_start_transaction(esp_lcd_i80_bus_t *bus, lcd_i80_trans_descriptor_t *trans_desc);
|
||||
static IRAM_ATTR void lcd_default_isr_handler(void *args);
|
||||
static void lcd_default_isr_handler(void *args);
|
||||
|
||||
struct esp_lcd_i80_bus_t {
|
||||
int bus_id; // Bus ID, index from 0
|
||||
@ -153,7 +153,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "select periph clock %d failed", bus_config->clk_src);
|
||||
// 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_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
|
||||
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
|
||||
ret = esp_intr_alloc_intrstatus(lcd_periph_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);
|
||||
@ -250,7 +250,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
||||
uint32_t pclk_prescale = bus->resolution_hz / io_config->pclk_hz;
|
||||
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= LCD_LL_CLOCK_PRESCALE_MAX, ESP_ERR_NOT_SUPPORTED, err, TAG,
|
||||
"prescaler can't satisfy PCLK clock %u", io_config->pclk_hz);
|
||||
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_MEM_ALLOC_CAPS);
|
||||
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_I80_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(i80_device, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 panel io");
|
||||
// create two queues for i80 device
|
||||
i80_device->trans_queue = xQueueCreate(io_config->trans_queue_depth, sizeof(lcd_i80_trans_descriptor_t *));
|
||||
@ -393,9 +393,8 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
|
||||
// switch devices if necessary
|
||||
lcd_i80_switch_devices(cur_device, next_device);
|
||||
// set data format
|
||||
lcd_ll_swap_data_byte_order(bus->hal.dev, false);
|
||||
lcd_ll_reverse_data_bit_order(bus->hal.dev, false);
|
||||
lcd_ll_reverse_data_8bits_order(bus->hal.dev, next_device->lcd_param_bits > bus->bus_width);
|
||||
lcd_ll_reverse_bit_order(bus->hal.dev, false);
|
||||
lcd_ll_swap_byte_order(bus->hal.dev, bus->bus_width, next_device->lcd_param_bits > bus->bus_width);
|
||||
bus->cur_trans = NULL;
|
||||
bus->cur_device = next_device;
|
||||
// package a transaction
|
||||
@ -643,9 +642,8 @@ IRAM_ATTR static void lcd_default_isr_handler(void *args)
|
||||
// switch devices if necessary
|
||||
lcd_i80_switch_devices(cur_device, next_device);
|
||||
// only reverse data bit/bytes for color data
|
||||
lcd_ll_reverse_data_bit_order(bus->hal.dev, next_device->flags.reverse_color_bits);
|
||||
lcd_ll_swap_data_byte_order(bus->hal.dev, next_device->flags.swap_color_bytes);
|
||||
lcd_ll_reverse_data_8bits_order(bus->hal.dev, false);
|
||||
lcd_ll_reverse_bit_order(bus->hal.dev, next_device->flags.reverse_color_bits);
|
||||
lcd_ll_swap_byte_order(bus->hal.dev, bus->bus_width, next_device->flags.swap_color_bytes);
|
||||
bus->cur_trans = trans_desc;
|
||||
bus->cur_device = next_device;
|
||||
// mount data to DMA links
|
||||
|
@ -1,7 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_i2c_lcd_panel.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_lcd unity driver)
|
||||
idf_component_register(SRCS ${srcs})
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_i2c_lcd")
|
||||
|
@ -1,7 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_i80_lcd_panel.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_lcd unity driver)
|
||||
idf_component_register(SRCS ${srcs})
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_i80_lcd")
|
||||
|
@ -1,7 +1,6 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_spi_lcd_panel.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_lcd unity driver)
|
||||
idf_component_register(SRCS ${srcs})
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_spi_lcd")
|
||||
|
@ -207,6 +207,7 @@ static inline void lcd_ll_start(lcd_cam_dev_t *dev)
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_stop(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_user.lcd_start = 0;
|
||||
@ -230,34 +231,32 @@ static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_reverse_data_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
static inline void lcd_ll_reverse_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
// whether to change LCD_DATA_out[N:0] to LCD_DATA_out[0:N]
|
||||
dev->lcd_user.lcd_bit_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to swap data byte order, i.e. data[15:0] -> data[7:0][15:8]
|
||||
* @brief Whether to swap adjacent two bytes
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width Bus width
|
||||
* @param en True to swap the byte order, False to not swap
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_swap_data_byte_order(lcd_cam_dev_t *dev, bool en)
|
||||
static inline void lcd_ll_swap_byte_order(lcd_cam_dev_t *dev, uint32_t width, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_byte_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to reverse the 8bits order
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_8bits_order = en;
|
||||
HAL_ASSERT(width == 8 || width == 16);
|
||||
if (width == 8) {
|
||||
// {B0}{B1}{B2}{B3} => {B1}{B0}{B3}{B2}
|
||||
dev->lcd_user.lcd_8bits_order = en;
|
||||
dev->lcd_user.lcd_byte_order = 0;
|
||||
} else if (width == 16) {
|
||||
// {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
|
||||
dev->lcd_user.lcd_byte_order = en;
|
||||
dev->lcd_user.lcd_8bits_order = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,6 +264,7 @@ static inline void lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t *dev, bool en)
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_fifo_reset(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_misc.lcd_afifo_reset = 1; // self clear
|
||||
|
@ -260,8 +260,8 @@ config SOC_LCD_I80_SUPPORTED
|
||||
default y
|
||||
|
||||
config SOC_LCD_I80_BUSES
|
||||
bool
|
||||
default y
|
||||
int
|
||||
default 2
|
||||
|
||||
config SOC_LCD_I80_BUS_WIDTH
|
||||
int
|
||||
|
@ -185,7 +185,7 @@
|
||||
/*-------------------------- LCD CAPS ----------------------------------------*/
|
||||
/* Notes: On esp32, LCD intel 8080 timing is generated by I2S peripheral */
|
||||
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
|
||||
#define SOC_LCD_I80_BUSES (1) /*!< Only I2S0 has LCD mode */
|
||||
#define SOC_LCD_I80_BUSES (2) /*!< Both I2S0/1 have LCD mode */
|
||||
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
|
||||
|
||||
/*-------------------------- LEDC CAPS ---------------------------------------*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -40,6 +40,37 @@ const lcd_signal_conn_t lcd_periph_signals = {
|
||||
I2S0O_DATA_OUT23_IDX,
|
||||
},
|
||||
.wr_sig = I2S0O_WS_OUT_IDX,
|
||||
},
|
||||
[1] = {
|
||||
.module = PERIPH_I2S1_MODULE,
|
||||
.irq_id = ETS_I2S1_INTR_SOURCE,
|
||||
.data_sigs = {
|
||||
I2S1O_DATA_OUT0_IDX,
|
||||
I2S1O_DATA_OUT1_IDX,
|
||||
I2S1O_DATA_OUT2_IDX,
|
||||
I2S1O_DATA_OUT3_IDX,
|
||||
I2S1O_DATA_OUT4_IDX,
|
||||
I2S1O_DATA_OUT5_IDX,
|
||||
I2S1O_DATA_OUT6_IDX,
|
||||
I2S1O_DATA_OUT7_IDX,
|
||||
I2S1O_DATA_OUT8_IDX,
|
||||
I2S1O_DATA_OUT9_IDX,
|
||||
I2S1O_DATA_OUT10_IDX,
|
||||
I2S1O_DATA_OUT11_IDX,
|
||||
I2S1O_DATA_OUT12_IDX,
|
||||
I2S1O_DATA_OUT13_IDX,
|
||||
I2S1O_DATA_OUT14_IDX,
|
||||
I2S1O_DATA_OUT15_IDX,
|
||||
I2S1O_DATA_OUT16_IDX,
|
||||
I2S1O_DATA_OUT17_IDX,
|
||||
I2S1O_DATA_OUT18_IDX,
|
||||
I2S1O_DATA_OUT19_IDX,
|
||||
I2S1O_DATA_OUT20_IDX,
|
||||
I2S1O_DATA_OUT21_IDX,
|
||||
I2S1O_DATA_OUT22_IDX,
|
||||
I2S1O_DATA_OUT23_IDX,
|
||||
},
|
||||
.wr_sig = I2S1O_WS_OUT_IDX,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -328,8 +328,8 @@ config SOC_LCD_I80_SUPPORTED
|
||||
default y
|
||||
|
||||
config SOC_LCD_I80_BUSES
|
||||
bool
|
||||
default y
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCD_I80_BUS_WIDTH
|
||||
int
|
||||
|
@ -173,7 +173,7 @@
|
||||
/*-------------------------- LCD CAPS ----------------------------------------*/
|
||||
/* Notes: On esp32-s2, LCD intel 8080 timing is generated by I2S peripheral */
|
||||
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
|
||||
#define SOC_LCD_I80_BUSES (1) /*!< Only I2S0 has LCD mode */
|
||||
#define SOC_LCD_I80_BUSES (1U) /*!< Only I2S0 has LCD mode */
|
||||
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
|
||||
|
||||
/*-------------------------- LEDC CAPS ---------------------------------------*/
|
||||
|
@ -528,12 +528,12 @@ config SOC_LCD_RGB_SUPPORTED
|
||||
default y
|
||||
|
||||
config SOC_LCD_I80_BUSES
|
||||
bool
|
||||
default y
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCD_RGB_PANELS
|
||||
bool
|
||||
default y
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCD_I80_BUS_WIDTH
|
||||
int
|
||||
|
@ -194,8 +194,8 @@
|
||||
/* Notes: On esp32-s3, I80 bus and RGB timing generator can't work at the same time */
|
||||
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
|
||||
#define SOC_LCD_RGB_SUPPORTED (1) /*!< RGB LCD is supported */
|
||||
#define SOC_LCD_I80_BUSES (1) /*!< Has one LCD Intel 8080 bus */
|
||||
#define SOC_LCD_RGB_PANELS (1) /*!< Support one RGB LCD panel */
|
||||
#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 (16) /*!< Intel 8080 bus width */
|
||||
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user