forked from espressif/esp-idf
lcd: allow execlusive use of i80 bus
This commit is contained in:
@@ -633,7 +633,7 @@ UT_046:
|
|||||||
|
|
||||||
UT_047:
|
UT_047:
|
||||||
extends: .unit_test_esp32s2_template
|
extends: .unit_test_esp32s2_template
|
||||||
parallel: 4
|
parallel: 5
|
||||||
tags:
|
tags:
|
||||||
- ESP32S2_IDF
|
- ESP32S2_IDF
|
||||||
- UT_T1_1
|
- UT_T1_1
|
||||||
|
@@ -165,7 +165,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus);
|
|||||||
* @brief Panel IO configuration structure, for intel 8080 interface
|
* @brief Panel IO configuration structure, for intel 8080 interface
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int cs_gpio_num; /*!< GPIO used for CS line */
|
int cs_gpio_num; /*!< GPIO used for CS line, set to -1 will declaim exclusively use of I80 bus */
|
||||||
unsigned int pclk_hz; /*!< Frequency of pixel clock */
|
unsigned int pclk_hz; /*!< Frequency of pixel clock */
|
||||||
size_t trans_queue_depth; /*!< Transaction queue size, larger queue, higher throughput */
|
size_t trans_queue_depth; /*!< Transaction queue size, larger queue, higher throughput */
|
||||||
bool (*on_color_trans_done)(esp_lcd_panel_io_handle_t panel_io, void *user_data, void *event_data); /*!< Callback, invoked when color data was tranferred done */
|
bool (*on_color_trans_done)(esp_lcd_panel_io_handle_t panel_io, void *user_data, void *event_data); /*!< Callback, invoked when color data was tranferred done */
|
||||||
|
@@ -71,6 +71,9 @@ struct esp_lcd_i80_bus_t {
|
|||||||
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
|
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
|
||||||
lcd_panel_io_i80_t *cur_device; // Current working device
|
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
|
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
|
||||||
|
} flags;
|
||||||
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
|
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -233,7 +236,17 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
|||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
lcd_panel_io_i80_t *i80_device = NULL;
|
lcd_panel_io_i80_t *i80_device = NULL;
|
||||||
|
bool bus_exclusive = false;
|
||||||
ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
// check if the bus has been configured as exclusive
|
||||||
|
portENTER_CRITICAL(&bus->spinlock);
|
||||||
|
if (!bus->flags.exclusive) {
|
||||||
|
bus->flags.exclusive = io_config->cs_gpio_num < 0;
|
||||||
|
} else {
|
||||||
|
bus_exclusive = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&bus->spinlock);
|
||||||
|
ESP_GOTO_ON_FALSE(!bus_exclusive, ESP_ERR_INVALID_STATE, err, TAG, "bus has been exclusively owned by device");
|
||||||
// because we set the I2S's left channel data same to right channel, so f_pclk = f_i2s/pclk_div/2
|
// because we set the I2S's left channel data same to right channel, so f_pclk = f_i2s/pclk_div/2
|
||||||
uint32_t pclk_prescale = bus->resolution_hz / 2 / io_config->pclk_hz;
|
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,
|
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= I2S_LL_BCK_MAX_PRESCALE, ESP_ERR_NOT_SUPPORTED, err, TAG,
|
||||||
@@ -269,10 +282,12 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
|||||||
i80_device->base.del = panel_io_i80_del;
|
i80_device->base.del = panel_io_i80_del;
|
||||||
i80_device->base.tx_param = panel_io_i80_tx_param;
|
i80_device->base.tx_param = panel_io_i80_tx_param;
|
||||||
i80_device->base.tx_color = panel_io_i80_tx_color;
|
i80_device->base.tx_color = panel_io_i80_tx_color;
|
||||||
// CS signal is controlled by software
|
if (io_config->cs_gpio_num >= 0) {
|
||||||
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high); // de-assert by default
|
// CS signal is controlled by software
|
||||||
gpio_set_direction(io_config->cs_gpio_num, GPIO_MODE_OUTPUT);
|
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high); // de-assert by default
|
||||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO);
|
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);
|
||||||
|
}
|
||||||
*ret_io = &(i80_device->base);
|
*ret_io = &(i80_device->base);
|
||||||
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d), pclk=%uHz", i80_device, bus->bus_id, i80_device->pclk_hz);
|
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d), pclk=%uHz", i80_device, bus->bus_id, i80_device->pclk_hz);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -646,10 +661,12 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
|
|||||||
if (next_device != cur_device) {
|
if (next_device != cur_device) {
|
||||||
// reconfigure PCLK for the new device
|
// reconfigure PCLK for the new device
|
||||||
i2s_ll_tx_set_bck_div_num(bus->hal.dev, next_device->clock_prescale);
|
i2s_ll_tx_set_bck_div_num(bus->hal.dev, next_device->clock_prescale);
|
||||||
if (cur_device) { // de-assert current device
|
if (cur_device && cur_device->cs_gpio_num >= 0) { // de-assert current device
|
||||||
gpio_set_level(cur_device->cs_gpio_num, !cur_device->flags.cs_active_high);
|
gpio_set_level(cur_device->cs_gpio_num, !cur_device->flags.cs_active_high);
|
||||||
}
|
}
|
||||||
gpio_set_level(next_device->cs_gpio_num, next_device->flags.cs_active_high); // select the next device
|
if (next_device->cs_gpio_num >= 0) {
|
||||||
|
gpio_set_level(next_device->cs_gpio_num, next_device->flags.cs_active_high); // select the next device
|
||||||
|
}
|
||||||
// the WR signal (a.k.a the PCLK) generated by I2S is low level in idle stage
|
// 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
|
// 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_signals.buses[bus->bus_id].wr_sig, !next_device->flags.pclk_idle_low, false);
|
||||||
|
@@ -65,6 +65,9 @@ struct esp_lcd_i80_bus_t {
|
|||||||
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
|
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
|
||||||
lcd_panel_io_i80_t *cur_device; // Current working device
|
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
|
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
|
||||||
|
} flags;
|
||||||
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
|
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,7 +222,17 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
|||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
lcd_panel_io_i80_t *i80_device = NULL;
|
lcd_panel_io_i80_t *i80_device = NULL;
|
||||||
|
bool bus_exclusive = false;
|
||||||
ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
// check if the bus has been configured as exclusive
|
||||||
|
portENTER_CRITICAL(&bus->spinlock);
|
||||||
|
if (!bus->flags.exclusive) {
|
||||||
|
bus->flags.exclusive = io_config->cs_gpio_num < 0;
|
||||||
|
} else {
|
||||||
|
bus_exclusive = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&bus->spinlock);
|
||||||
|
ESP_GOTO_ON_FALSE(!bus_exclusive, ESP_ERR_INVALID_STATE, err, TAG, "bus has been exclusively owned by device");
|
||||||
// check if pixel clock setting is valid
|
// check if pixel clock setting is valid
|
||||||
uint32_t pclk_prescale = bus->resolution_hz / io_config->pclk_hz;
|
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,
|
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= LCD_LL_CLOCK_PRESCALE_MAX, ESP_ERR_NOT_SUPPORTED, err, TAG,
|
||||||
@@ -261,9 +274,11 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
|
|||||||
i80_device->base.tx_color = panel_io_i80_tx_color;
|
i80_device->base.tx_color = panel_io_i80_tx_color;
|
||||||
// we only configure the CS GPIO as output, don't connect to the peripheral signal at the moment
|
// we only configure the CS GPIO as output, don't connect to the peripheral signal at the moment
|
||||||
// we will connect the CS GPIO to peripheral signal when switching devices in lcd_i80_switch_devices()
|
// we will connect the CS GPIO to peripheral signal when switching devices in lcd_i80_switch_devices()
|
||||||
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high);
|
if (io_config->cs_gpio_num >= 0) {
|
||||||
gpio_set_direction(io_config->cs_gpio_num, GPIO_MODE_OUTPUT);
|
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high);
|
||||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO);
|
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);
|
||||||
|
}
|
||||||
*ret_io = &(i80_device->base);
|
*ret_io = &(i80_device->base);
|
||||||
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id);
|
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -541,13 +556,15 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
|
|||||||
// configure DC line level for the new device
|
// configure DC line level for the new device
|
||||||
lcd_ll_set_dc_level(bus->hal.dev, next_device->dc_levels.dc_idle_level, next_device->dc_levels.dc_cmd_level,
|
lcd_ll_set_dc_level(bus->hal.dev, next_device->dc_levels.dc_idle_level, next_device->dc_levels.dc_cmd_level,
|
||||||
next_device->dc_levels.dc_dummy_level, next_device->dc_levels.dc_data_level);
|
next_device->dc_levels.dc_dummy_level, next_device->dc_levels.dc_data_level);
|
||||||
if (cur_device) {
|
if (cur_device && cur_device->cs_gpio_num >= 0) {
|
||||||
// disconnect current CS GPIO from peripheral signal
|
// disconnect current CS GPIO from peripheral signal
|
||||||
esp_rom_gpio_connect_out_signal(cur_device->cs_gpio_num, SIG_GPIO_OUT_IDX, false, false);
|
esp_rom_gpio_connect_out_signal(cur_device->cs_gpio_num, SIG_GPIO_OUT_IDX, false, false);
|
||||||
}
|
}
|
||||||
// connect CS signal to the new device
|
if (next_device->cs_gpio_num >= 0) {
|
||||||
esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_signals.buses[bus->bus_id].cs_sig,
|
// connect CS signal to the new device
|
||||||
next_device->flags.cs_active_high, false);
|
esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_signals.buses[bus->bus_id].cs_sig,
|
||||||
|
next_device->flags.cs_active_high, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -176,6 +176,41 @@ TEST_CASE("lcd i80 bus and device allocation", "[lcd]")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("lcd i80 bus exclusively owned by one device", "[lcd]")
|
||||||
|
{
|
||||||
|
esp_lcd_i80_bus_handle_t i80_bus_handle = NULL;
|
||||||
|
esp_lcd_i80_bus_config_t bus_config = {
|
||||||
|
.dc_gpio_num = TEST_LCD_DC_GPIO,
|
||||||
|
.wr_gpio_num = TEST_LCD_PCLK_GPIO,
|
||||||
|
.data_gpio_nums = {
|
||||||
|
TEST_LCD_DATA0_GPIO,
|
||||||
|
TEST_LCD_DATA1_GPIO,
|
||||||
|
TEST_LCD_DATA2_GPIO,
|
||||||
|
TEST_LCD_DATA3_GPIO,
|
||||||
|
TEST_LCD_DATA4_GPIO,
|
||||||
|
TEST_LCD_DATA5_GPIO,
|
||||||
|
TEST_LCD_DATA6_GPIO,
|
||||||
|
TEST_LCD_DATA7_GPIO,
|
||||||
|
},
|
||||||
|
.bus_width = 8,
|
||||||
|
.max_transfer_bytes = TEST_LCD_H_RES * 40 * sizeof(uint16_t)
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus_handle));
|
||||||
|
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||||
|
esp_lcd_panel_io_i80_config_t io_config = {
|
||||||
|
.cs_gpio_num = -1, // own the bus exclusively
|
||||||
|
.pclk_hz = 5000000,
|
||||||
|
.trans_queue_depth = 4,
|
||||||
|
.lcd_cmd_bits = 8,
|
||||||
|
.lcd_param_bits = 8,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(esp_lcd_new_panel_io_i80(i80_bus_handle, &io_config, &io_handle));
|
||||||
|
io_config.cs_gpio_num = 0;
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_lcd_new_panel_io_i80(i80_bus_handle, &io_config, &io_handle));
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
|
||||||
|
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus_handle));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("lcd panel i80 io test", "[lcd]")
|
TEST_CASE("lcd panel i80 io test", "[lcd]")
|
||||||
{
|
{
|
||||||
esp_lcd_i80_bus_handle_t i80_bus = NULL;
|
esp_lcd_i80_bus_handle_t i80_bus = NULL;
|
||||||
|
Reference in New Issue
Block a user