add some code
This commit is contained in:
@@ -0,0 +1 @@
|
||||
213d5c4a3c322a48c9f993d6bc2372206d34af95db34e87a3e02601bdcad7ce8
|
||||
@@ -0,0 +1,18 @@
|
||||
# ChangeLog
|
||||
|
||||
## v1.0.0 - 2023-06-20
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* esp_lcd_panel_io_3wire_spi:
|
||||
* Support bit-banging the 3-wire SPI LCD protocol (without D/C and MISO lines)
|
||||
* Support for GPIO and IO expander
|
||||
* Support for 8-bit/16-bit/24-bit/32-bit data write (without D/C bit)
|
||||
* Support for 9-bit/17-bit/25-bit/33-bit data write (including D/C bit)
|
||||
|
||||
## v1.0.1 - 2023-09-11
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* esp_lcd_panel_io_3wire_spi:
|
||||
* fix test_apps build error
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T15:57:01.835752+00:00", "files": [{"path": "CMakeLists.txt", "size": 249, "hash": "5388d6f9259c00b95ea4b584a778cc41c9dd9b747d0c2568a70479cb3a567a9b"}, {"path": "CHANGELOG.md", "size": 460, "hash": "62e47b6b3cf8acb79a8a674dc9aa03bed010d39b3ae5557a775cfcf81cf84001"}, {"path": "idf_component.yml", "size": 424, "hash": "24726706fc0601674bcbf038468364682da6110fc24d9a7953ce8d3bfe96b5d8"}, {"path": "README.md", "size": 3445, "hash": "172334f877dc9e0cefd4427338c36af2ec915846faf0e1ebfe0356e71535dc9a"}, {"path": "esp_lcd_panel_io_3wire_spi.c", "size": 18001, "hash": "84ecbeb41525fa81ce2352629e631dfc9fdac7db5c58de4e71e1f7e4ad589f45"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "include/esp_lcd_panel_io_additions.h", "size": 3682, "hash": "cf86c8cf03adedb1830a0456f135f1f041ffc0c1492e197bdb3c06aa5cf58f46"}, {"path": "test_apps/CMakeLists.txt", "size": 319, "hash": "035b992886542c7ed1d9746b597f4fcb334c10726cbdd95f67b1c911591c2ac3"}, {"path": "test_apps/sdkconfig.defaults", "size": 256, "hash": "0f781fe2bf2884d6f09049d73b20e5214eb847647ef3cad4d313cd73966b7c65"}, {"path": "test_apps/pytest_esp_lcd_panel_io_addtions.py", "size": 196, "hash": "db0c999ace0e4bdf832f6bb4331bba3216dd86abbec737e71491cdb6fe1ec296"}, {"path": "test_apps/main/CMakeLists.txt", "size": 65, "hash": "f45243490b43afdcffd0bea9de189987227d95a9112ccb1bdf49714de3a34ef6"}, {"path": "test_apps/main/idf_component.yml", "size": 190, "hash": "9a472a503acf4b05efec8f4b10dc960d9ed7bd40d3f376c55db1799f62d91d29"}, {"path": "test_apps/main/test_esp_lcd_panel_io_additions.c", "size": 9805, "hash": "6d0bbf7cc8514f55691803acb9c833e00b8b24e2924b9ebafe27fdd03ef36a74"}]}
|
||||
@@ -0,0 +1,12 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"esp_lcd_panel_io_3wire_spi.c"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
PRIV_REQUIRES
|
||||
"driver"
|
||||
REQUIRES
|
||||
"esp_lcd")
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
@@ -0,0 +1,87 @@
|
||||
[](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions)
|
||||
|
||||
# Component: ESP_LCD_PANEL_IO_ADDITIONS
|
||||
|
||||
This component supplements the [esp_lcd](https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/include/esp_lcd_panel_io.h) component in ESP-IDF and offers additional functionality through `esp_lcd_panel_io_*()`. It provides the following functions:
|
||||
|
||||
* **esp_lcd_new_panel_io_3wire_spi()**: This function utilizes GPIO or IO expander to perform bit-banging for the 3-wire SPI interface (without D/C and MISO lines). It is specifically designed for the [3 Wire SPI + Parallel RGB Interface](https://focuslcds.com/3-wire-spi-parallel-rgb-interface-fan4213/).
|
||||
|
||||
## Add to project
|
||||
|
||||
Please use the component manager command `idf.py add-dependency` to add the `esp_lcd_panel_io_additions` to your project's dependency, during the `CMake` step the component will be downloaded automatically.
|
||||
|
||||
```
|
||||
idf.py add-dependency esp_lcd_panel_io_additions==1.0.0
|
||||
```
|
||||
|
||||
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
|
||||
|
||||
## Example use
|
||||
|
||||
### ESP_LCD_PANEL_IO_3WIRE_SPI
|
||||
|
||||
Initialization of the panel IO.
|
||||
|
||||
```
|
||||
spi_line_config_t spi_line = {
|
||||
.cs_io_type = IO_TYPE_EXPANDER,
|
||||
.cs_gpio_num = IO_EXPANDER_PIN_NUM_1,
|
||||
.scl_io_type = IO_TYPE_GPIO,
|
||||
.scl_gpio_num = GPIO_NUM_9,
|
||||
.sda_io_type = IO_TYPE_GPIO,
|
||||
.sda_gpio_num = GPIO_NUM_10,
|
||||
.io_expander = io_expander, // Created by the user
|
||||
};
|
||||
esp_lcd_panel_io_3wire_spi_config_t io_config = {
|
||||
.line_config = spi_line,
|
||||
.expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX,
|
||||
.spi_mode = 0,
|
||||
.lcd_cmd_bytes = 1,
|
||||
.lcd_param_bytes = 1,
|
||||
.flags = {
|
||||
.use_dc_bit = 1,
|
||||
.dc_zero_on_data = 0,
|
||||
.lsb_first = 0,
|
||||
.cs_high_active = 0,
|
||||
.del_keep_cs_inactive = 0,
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_io_handle_t panel_io = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &panel_io));
|
||||
```
|
||||
|
||||
Write LCD command and parameters through the panel IO.
|
||||
|
||||
```
|
||||
esp_lcd_panel_io_tx_param(panel_io, lcd_cmd, lcd_param, bytes_of_lcd_param);
|
||||
```
|
||||
|
||||
Here is an example of using it to initialize the RGB LCD panel.
|
||||
|
||||
```
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t data[12];
|
||||
uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds.
|
||||
} lcd_init_cmd_t;
|
||||
|
||||
static const lcd_init_cmd_t vendor_specific_init[] = {
|
||||
...
|
||||
{0x62, {0x38, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x38, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12},
|
||||
{0x63, {0x38, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12},
|
||||
...
|
||||
{0, {0}, 0xff},
|
||||
};
|
||||
|
||||
static void panel_init(esp_lcd_panel_io_handle_t panel_io)
|
||||
{
|
||||
// vendor specific initialization, it can be different between manufacturers
|
||||
// should consult the LCD supplier for initialization sequence code
|
||||
int i = 0;
|
||||
while (vendor_specific_init[i].data_bytes != 0xff) {
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_io_tx_param(panel_io, vendor_specific_init[i].cmd, vendor_specific_init[i].data,
|
||||
vendor_specific_init[i].data_bytes));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
|
||||
#include "esp_lcd_panel_io_additions.h"
|
||||
|
||||
#define LCD_CMD_BYTES_MAX (sizeof(uint32_t)) // Maximum number of bytes for LCD command
|
||||
#define LCD_PARAM_BYTES_MAX (sizeof(uint32_t)) // Maximum number of bytes for LCD parameter
|
||||
|
||||
#define DATA_DC_BIT_0 (0) // DC bit = 0
|
||||
#define DATA_DC_BIT_1 (1) // DC bit = 1
|
||||
#define DATA_NO_DC_BIT (2) // No DC bit
|
||||
#define WRITE_ORDER_LSB_MASK (0x01) // Bit mask for LSB first write order
|
||||
#define WRITE_ORDER_MSB_MASK (0x80) // Bit mask for MSB first write order
|
||||
|
||||
/**
|
||||
* @brief Enumeration of SPI lines
|
||||
*/
|
||||
typedef enum {
|
||||
CS = 0,
|
||||
SCL,
|
||||
SDA,
|
||||
} spi_line_t;
|
||||
|
||||
/**
|
||||
* @brief Panel IO instance for 3-wire SPI interface
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_t base; /*!< Base class of generic lcd panel io */
|
||||
panel_io_type_t cs_io_type; /*!< IO type of CS line */
|
||||
int cs_io_num; /*!< IO used for CS line */
|
||||
panel_io_type_t scl_io_type; /*!< IO type of SCL line */
|
||||
int scl_io_num; /*!< IO used for SCL line */
|
||||
panel_io_type_t sda_io_type; /*!< IO type of SDA line */
|
||||
int sda_io_num; /*!< GPIO used for SDA line */
|
||||
esp_io_expander_handle_t io_expander; /*!< IO expander handle, set to NULL if not used */
|
||||
uint32_t scl_half_period_us; /*!< SCL half period in us */
|
||||
uint32_t lcd_cmd_bytes: 3; /*!< Bytes of LCD command (1 ~ 4) */
|
||||
uint32_t cmd_dc_bit: 2; /*!< DC bit of command */
|
||||
uint32_t lcd_param_bytes: 3; /*!< Bytes of LCD parameter (1 ~ 4) */
|
||||
uint32_t param_dc_bit: 2; /*!< DC bit of parameter */
|
||||
uint32_t write_order_mask: 8; /*!< Bit mask of write order */
|
||||
struct {
|
||||
uint32_t cs_high_active: 1; /*!< If this flag is enabled, CS line is high active */
|
||||
uint32_t sda_scl_idle_high: 1; /*!< If this flag is enabled, SDA and SCL line are high when idle */
|
||||
uint32_t scl_active_rising_edge: 1; /*!< If this flag is enabled, SCL line is active on rising edge */
|
||||
uint32_t del_keep_cs_inactive: 1; /*!< If this flag is enabled, keep CS line inactive even if panel_io is deleted */
|
||||
} flags;
|
||||
} esp_lcd_panel_io_3wire_spi_t;
|
||||
|
||||
static const char *TAG = "lcd_panel.io.3wire_spi";
|
||||
|
||||
static esp_err_t panel_io_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size);
|
||||
static esp_err_t panel_io_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size);
|
||||
static esp_err_t panel_io_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size);
|
||||
static esp_err_t panel_io_del(esp_lcd_panel_io_t *io);
|
||||
static esp_err_t panel_io_register_event_callbacks(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_io_callbacks_t *cbs, void *user_ctx);
|
||||
|
||||
static esp_err_t set_line_level(esp_lcd_panel_io_3wire_spi_t *panel_io, spi_line_t line, uint32_t level);
|
||||
static esp_err_t reset_line_io(esp_lcd_panel_io_3wire_spi_t *panel_io, spi_line_t line);
|
||||
static esp_err_t spi_write_package(esp_lcd_panel_io_3wire_spi_t *panel_io, bool is_cmd, uint32_t data);
|
||||
|
||||
esp_err_t esp_lcd_new_panel_io_3wire_spi(const esp_lcd_panel_io_3wire_spi_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io_config->expect_clk_speed <= PANEL_IO_3WIRE_SPI_CLK_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid Clock frequency");
|
||||
ESP_RETURN_ON_FALSE(io_config->lcd_cmd_bytes > 0 && io_config->lcd_cmd_bytes <= LCD_CMD_BYTES_MAX, ESP_ERR_INVALID_ARG,
|
||||
TAG, "Invalid LCD command bytes");
|
||||
ESP_RETURN_ON_FALSE(io_config->lcd_param_bytes > 0 && io_config->lcd_param_bytes <= LCD_PARAM_BYTES_MAX, ESP_ERR_INVALID_ARG,
|
||||
TAG, "Invalid LCD parameter bytes");
|
||||
|
||||
const spi_line_config_t *line_config = &io_config->line_config;
|
||||
ESP_RETURN_ON_FALSE(line_config->io_expander || (line_config->cs_io_type == IO_TYPE_GPIO &&
|
||||
line_config->scl_io_type == IO_TYPE_GPIO && line_config->sda_io_type == IO_TYPE_GPIO),
|
||||
ESP_ERR_INVALID_ARG, TAG, "IO Expander handle is required if any IO is not gpio");
|
||||
|
||||
esp_lcd_panel_io_3wire_spi_t *panel_io = calloc(1, sizeof(esp_lcd_panel_io_3wire_spi_t));
|
||||
ESP_RETURN_ON_FALSE(panel_io, ESP_ERR_NO_MEM, TAG, "No memory");
|
||||
|
||||
panel_io->cs_io_type = line_config->cs_io_type;
|
||||
panel_io->cs_io_num = line_config->cs_gpio_num;
|
||||
panel_io->scl_io_type = line_config->scl_io_type;
|
||||
panel_io->scl_io_num = line_config->scl_gpio_num;
|
||||
panel_io->sda_io_type = line_config->sda_io_type;
|
||||
panel_io->sda_io_num = line_config->sda_gpio_num;
|
||||
panel_io->io_expander = line_config->io_expander;
|
||||
uint32_t expect_clk_speed = io_config->expect_clk_speed ? io_config->expect_clk_speed : PANEL_IO_3WIRE_SPI_CLK_MAX;
|
||||
panel_io->scl_half_period_us = 1000000 / (expect_clk_speed * 2);
|
||||
panel_io->lcd_cmd_bytes = io_config->lcd_cmd_bytes;
|
||||
panel_io->lcd_param_bytes = io_config->lcd_param_bytes;
|
||||
if (io_config->flags.use_dc_bit) {
|
||||
panel_io->param_dc_bit = io_config->flags.dc_zero_on_data ? DATA_DC_BIT_0 : DATA_DC_BIT_1;
|
||||
panel_io->cmd_dc_bit = io_config->flags.dc_zero_on_data ? DATA_DC_BIT_1 : DATA_DC_BIT_0;
|
||||
} else {
|
||||
panel_io->param_dc_bit = DATA_NO_DC_BIT;
|
||||
panel_io->cmd_dc_bit = DATA_NO_DC_BIT;
|
||||
}
|
||||
panel_io->write_order_mask = io_config->flags.lsb_first ? WRITE_ORDER_LSB_MASK : WRITE_ORDER_MSB_MASK;
|
||||
panel_io->flags.cs_high_active = io_config->flags.cs_high_active;
|
||||
panel_io->flags.del_keep_cs_inactive = io_config->flags.del_keep_cs_inactive;
|
||||
panel_io->flags.sda_scl_idle_high = io_config->spi_mode & 0x1;
|
||||
if (panel_io->flags.sda_scl_idle_high) {
|
||||
panel_io->flags.scl_active_rising_edge = (io_config->spi_mode & 0x2) ? 1 : 0;
|
||||
} else {
|
||||
panel_io->flags.scl_active_rising_edge = (io_config->spi_mode & 0x2) ? 0 : 1;
|
||||
}
|
||||
|
||||
panel_io->base.rx_param = panel_io_rx_param;
|
||||
panel_io->base.tx_param = panel_io_tx_param;
|
||||
panel_io->base.tx_color = panel_io_tx_color;
|
||||
panel_io->base.del = panel_io_del;
|
||||
panel_io->base.register_event_callbacks = panel_io_register_event_callbacks;
|
||||
|
||||
// Get GPIO mask and IO expander pin mask
|
||||
esp_err_t ret = ESP_OK;
|
||||
int64_t gpio_mask = 0;
|
||||
uint32_t expander_pin_mask = 0;
|
||||
if (panel_io->cs_io_type == IO_TYPE_GPIO) {
|
||||
gpio_mask |= BIT64(panel_io->cs_io_num);
|
||||
} else {
|
||||
expander_pin_mask |= panel_io->cs_io_num;
|
||||
}
|
||||
if (panel_io->scl_io_type == IO_TYPE_GPIO) {
|
||||
gpio_mask |= BIT64(panel_io->scl_io_num);
|
||||
} else {
|
||||
expander_pin_mask |= panel_io->scl_io_num;
|
||||
}
|
||||
if (panel_io->sda_io_type == IO_TYPE_GPIO) {
|
||||
gpio_mask |= BIT64(panel_io->sda_io_num);
|
||||
} else {
|
||||
expander_pin_mask |= panel_io->sda_io_num;
|
||||
}
|
||||
// Configure GPIOs
|
||||
if (gpio_mask) {
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&((gpio_config_t) {
|
||||
.pin_bit_mask = gpio_mask,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
})), err, TAG, "GPIO config failed");
|
||||
}
|
||||
// Configure pins of IO expander
|
||||
if (expander_pin_mask) {
|
||||
ESP_GOTO_ON_ERROR(esp_io_expander_set_dir(panel_io->io_expander, expander_pin_mask, IO_EXPANDER_OUTPUT), err,
|
||||
TAG, "Expander set dir failed");
|
||||
}
|
||||
|
||||
// Set CS, SCL and SDA to idle level
|
||||
uint32_t cs_idle_level = panel_io->flags.cs_high_active ? 0 : 1;
|
||||
uint32_t sda_scl_idle_level = panel_io->flags.sda_scl_idle_high ? 1 : 0;
|
||||
ESP_GOTO_ON_ERROR(set_line_level(panel_io, CS, cs_idle_level), err, TAG, "Set CS level failed");
|
||||
ESP_GOTO_ON_ERROR(set_line_level(panel_io, SCL, sda_scl_idle_level), err, TAG, "Set SCL level failed");
|
||||
ESP_GOTO_ON_ERROR(set_line_level(panel_io, SDA, sda_scl_idle_level), err, TAG, "Set SDA level failed");
|
||||
|
||||
*ret_io = (esp_lcd_panel_io_handle_t)panel_io;
|
||||
ESP_LOGI(TAG, "Panel IO create success, version: %d.%d.%d", ESP_LCD_PANEL_IO_ADDITIONS_VER_MAJOR,
|
||||
ESP_LCD_PANEL_IO_ADDITIONS_VER_MINOR, ESP_LCD_PANEL_IO_ADDITIONS_VER_PATCH);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (gpio_mask) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (gpio_mask & BIT64(i)) {
|
||||
gpio_reset_pin(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expander_pin_mask) {
|
||||
esp_io_expander_set_dir(panel_io->io_expander, expander_pin_mask, IO_EXPANDER_INPUT);
|
||||
}
|
||||
free(panel_io);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_io_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size)
|
||||
{
|
||||
esp_lcd_panel_io_3wire_spi_t *panel_io = __containerof(io, esp_lcd_panel_io_3wire_spi_t, base);
|
||||
|
||||
// Send command
|
||||
if (lcd_cmd >= 0) {
|
||||
ESP_RETURN_ON_ERROR(spi_write_package(panel_io, true, lcd_cmd), TAG, "SPI write package failed");
|
||||
}
|
||||
|
||||
// Send parameter
|
||||
if (param != NULL && param_size > 0) {
|
||||
uint32_t param_data = 0;
|
||||
uint32_t param_bytes = panel_io->lcd_param_bytes;
|
||||
size_t param_count = param_size / param_bytes;
|
||||
|
||||
// Iteratively get parameter packages and send them one by one
|
||||
for (int i = 0; i < param_count; i++) {
|
||||
param_data = 0;
|
||||
for (int j = 0; j < param_bytes; j++) {
|
||||
param_data |= ((uint8_t *)param)[i * param_bytes + j] << (j * 8);
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(spi_write_package(panel_io, false, param_data), TAG, "SPI write package failed");
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_io_del(esp_lcd_panel_io_t *io)
|
||||
{
|
||||
esp_lcd_panel_io_3wire_spi_t *panel_io = __containerof(io, esp_lcd_panel_io_3wire_spi_t, base);
|
||||
|
||||
if (!panel_io->flags.del_keep_cs_inactive) {
|
||||
ESP_RETURN_ON_ERROR(reset_line_io(panel_io, CS), TAG, "Reset CS line failed");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Delete but keep CS line inactive");
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(reset_line_io(panel_io, SCL), TAG, "Reset SCL line failed");
|
||||
ESP_RETURN_ON_ERROR(reset_line_io(panel_io, SDA), TAG, "Reset SDA line failed");
|
||||
free(panel_io);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is not implemented and only for compatibility
|
||||
*/
|
||||
static esp_err_t panel_io_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size)
|
||||
{
|
||||
ESP_LOGE(TAG, "Rx param is not supported");
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is not implemented and only for compatibility
|
||||
*/
|
||||
static esp_err_t panel_io_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size)
|
||||
{
|
||||
ESP_LOGE(TAG, "Tx color is not supported");
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function is not implemented and only for compatibility
|
||||
*/
|
||||
static esp_err_t panel_io_register_event_callbacks(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_io_callbacks_t *cbs, void *user_ctx)
|
||||
{
|
||||
ESP_LOGE(TAG, "Register event callbacks is not supported");
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the level of specified line.
|
||||
*
|
||||
* This function can use GPIO or IO expander according to the type of line
|
||||
*
|
||||
* @param[in] panel_io Pointer to panel IO instance
|
||||
* @param[in] line Target line
|
||||
* @param[out] level Target level, 0 - Low, 1 - High
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - Others: Fail
|
||||
*/
|
||||
static esp_err_t set_line_level(esp_lcd_panel_io_3wire_spi_t *panel_io, spi_line_t line, uint32_t level)
|
||||
{
|
||||
panel_io_type_t line_type = IO_TYPE_GPIO;
|
||||
int line_io = 0;
|
||||
switch (line) {
|
||||
case CS:
|
||||
line_type = panel_io->cs_io_type;
|
||||
line_io = panel_io->cs_io_num;
|
||||
break;
|
||||
case SCL:
|
||||
line_type = panel_io->scl_io_type;
|
||||
line_io = panel_io->scl_io_num;
|
||||
break;
|
||||
case SDA:
|
||||
line_type = panel_io->sda_io_type;
|
||||
line_io = panel_io->sda_io_num;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (line_type == IO_TYPE_GPIO) {
|
||||
return gpio_set_level(line_io, level);
|
||||
} else {
|
||||
return esp_io_expander_set_level(panel_io->io_expander, (esp_io_expander_pin_num_t)line_io, level != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the IO of specified line
|
||||
*
|
||||
* This function can use GPIO or IO expander according to the type of line
|
||||
*
|
||||
* @param[in] panel_io Pointer to panel IO instance
|
||||
* @param[in] line Target line
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - Others: Fail
|
||||
*/
|
||||
static esp_err_t reset_line_io(esp_lcd_panel_io_3wire_spi_t *panel_io, spi_line_t line)
|
||||
{
|
||||
panel_io_type_t line_type = IO_TYPE_GPIO;
|
||||
int line_io = 0;
|
||||
switch (line) {
|
||||
case CS:
|
||||
line_type = panel_io->cs_io_type;
|
||||
line_io = panel_io->cs_io_num;
|
||||
break;
|
||||
case SCL:
|
||||
line_type = panel_io->scl_io_type;
|
||||
line_io = panel_io->scl_io_num;
|
||||
break;
|
||||
case SDA:
|
||||
line_type = panel_io->sda_io_type;
|
||||
line_io = panel_io->sda_io_num;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (line_type == IO_TYPE_GPIO) {
|
||||
return gpio_reset_pin(line_io);
|
||||
} else {
|
||||
return esp_io_expander_set_dir(panel_io->io_expander, (esp_io_expander_pin_num_t)line_io, IO_EXPANDER_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delay for given microseconds
|
||||
*
|
||||
* @note This function uses `esp_rom_delay_us()` for delays < 1000us and `vTaskDelay()` for longer delays.
|
||||
*
|
||||
* @param[in] delay_us Delay time in microseconds
|
||||
*
|
||||
*/
|
||||
static void delay_us(uint32_t delay_us)
|
||||
{
|
||||
if (delay_us >= 1000) {
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_us / 1000));
|
||||
} else {
|
||||
esp_rom_delay_us(delay_us);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write one byte to LCD panel
|
||||
*
|
||||
* @param[in] panel_io Pointer to panel IO instance
|
||||
* @param[in] dc_bit DC bit
|
||||
* @param[in] data 8-bit data to write
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - Others: Fail
|
||||
*/
|
||||
static esp_err_t spi_write_byte(esp_lcd_panel_io_3wire_spi_t *panel_io, int dc_bit, uint8_t data)
|
||||
{
|
||||
uint16_t data_temp = data;
|
||||
uint8_t data_bits = (dc_bit != DATA_NO_DC_BIT) ? 9 : 8;
|
||||
uint16_t write_order_mask = panel_io->write_order_mask;
|
||||
uint32_t scl_active_befor_level = panel_io->flags.scl_active_rising_edge ? 0 : 1;
|
||||
uint32_t scl_active_after_level = !scl_active_befor_level;
|
||||
uint32_t scl_half_period_us = panel_io->scl_half_period_us;
|
||||
|
||||
for (uint8_t i = 0; i < data_bits; i++) {
|
||||
// Send DC bit first
|
||||
if (data_bits == 9 && i == 0) {
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SDA, dc_bit), TAG, "Set SDA level failed");
|
||||
} else { // Then send data bit
|
||||
// SDA set to data bit
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SDA, data_temp & write_order_mask), TAG, "Set SDA level failed");
|
||||
// Get next bit
|
||||
data_temp = (write_order_mask == WRITE_ORDER_LSB_MASK) ? data_temp >> 1 : data_temp << 1;
|
||||
}
|
||||
// Generate SCL active edge
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SCL, scl_active_befor_level), TAG, "Set SCL level failed");
|
||||
delay_us(scl_half_period_us);
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SCL, scl_active_after_level), TAG, "Set SCL level failed");
|
||||
delay_us(scl_half_period_us);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a package of data to LCD panel in big-endian order
|
||||
*
|
||||
* @param[in] panel_io Pointer to panel IO instance
|
||||
* @param[in] is_cmd True for command, false for data
|
||||
* @param[in] data Data to write, with
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - Others: Fail
|
||||
*/
|
||||
static esp_err_t spi_write_package(esp_lcd_panel_io_3wire_spi_t *panel_io, bool is_cmd, uint32_t data)
|
||||
{
|
||||
uint32_t data_bytes = is_cmd ? panel_io->lcd_cmd_bytes : panel_io->lcd_param_bytes;
|
||||
uint32_t cs_idle_level = panel_io->flags.cs_high_active ? 0 : 1;
|
||||
uint32_t sda_scl_idle_level = panel_io->flags.sda_scl_idle_high ? 1 : 0;
|
||||
uint32_t scl_active_befor_level = panel_io->flags.scl_active_rising_edge ? 0 : 1;
|
||||
uint32_t time_us = panel_io->scl_half_period_us;
|
||||
// Swap command bytes order due to different endianness
|
||||
uint32_t swap_data = SPI_SWAP_DATA_TX(data, data_bytes * 8);
|
||||
int data_dc_bit = is_cmd ? panel_io->cmd_dc_bit : panel_io->param_dc_bit;
|
||||
|
||||
// CS active
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, CS, !cs_idle_level), TAG, "Set CS level failed");
|
||||
delay_us(time_us);
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SCL, scl_active_befor_level), TAG, "Set SCL level failed");
|
||||
// Send data byte by byte
|
||||
for (int i = 0; i < data_bytes; i++) {
|
||||
// Only set DC bit for the first byte
|
||||
if (i == 0) {
|
||||
ESP_RETURN_ON_ERROR(spi_write_byte(panel_io, data_dc_bit, swap_data & 0xff), TAG, "SPI write byte failed");
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(spi_write_byte(panel_io, DATA_NO_DC_BIT, swap_data & 0xff), TAG, "SPI write byte failed");
|
||||
}
|
||||
swap_data >>= 8;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SCL, sda_scl_idle_level), TAG, "Set SCL level failed");
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, SDA, sda_scl_idle_level), TAG, "Set SDA level failed");
|
||||
delay_us(time_us);
|
||||
// CS inactive
|
||||
ESP_RETURN_ON_ERROR(set_line_level(panel_io, CS, cs_idle_level), TAG, "Set CS level failed");
|
||||
delay_us(time_us);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
dependencies:
|
||||
cmake_utilities:
|
||||
version: 0.*
|
||||
esp_io_expander:
|
||||
public: true
|
||||
version: ^1
|
||||
idf:
|
||||
version: '>=4.4.2'
|
||||
description: Additional ESP LCD Panel IO
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: https://github.com/espressif/esp-iot-solution.git
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_panel_io_additions
|
||||
version: 1.0.1
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_io_expander.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Maximum SPI clock speed
|
||||
#define PANEL_IO_3WIRE_SPI_CLK_MAX (500 * 1000UL)
|
||||
|
||||
/**
|
||||
* @brief Panel IO type, use GPIO or IO expander
|
||||
*/
|
||||
typedef enum {
|
||||
IO_TYPE_GPIO = 0,
|
||||
IO_TYPE_EXPANDER,
|
||||
} panel_io_type_t;
|
||||
|
||||
/**
|
||||
* @brief SPI line configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
panel_io_type_t cs_io_type; /*!< IO type of CS line */
|
||||
union {
|
||||
int cs_gpio_num; /*!< GPIO used for CS line */
|
||||
esp_io_expander_pin_num_t cs_expander_pin; /*!< IO expander pin used for CS line */
|
||||
};
|
||||
panel_io_type_t scl_io_type; /*!< IO type of SCL line */
|
||||
union {
|
||||
int scl_gpio_num; /*!< GPIO used for SCL line */
|
||||
esp_io_expander_pin_num_t scl_expander_pin; /*!< IO expander pin used for SCL line */
|
||||
};
|
||||
panel_io_type_t sda_io_type; /*!< IO type of SDA line */
|
||||
union {
|
||||
int sda_gpio_num; /*!< GPIO used for SDA line */
|
||||
esp_io_expander_pin_num_t sda_expander_pin; /*!< IO expander pin used for SDA line */
|
||||
};
|
||||
esp_io_expander_handle_t io_expander; /*!< IO expander handle, set to NULL if not used */
|
||||
} spi_line_config_t;
|
||||
|
||||
/**
|
||||
* @brief Panel IO configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
spi_line_config_t line_config; /*!< SPI line configuration */
|
||||
uint32_t expect_clk_speed; /*!< Expected SPI clock speed, in Hz (1 ~ 500000
|
||||
* If this value is 0, it will be set to `PANEL_IO_3WIRE_SPI_CLK_MAX` by defaul
|
||||
* The actual frequency may be very different due to the limitation of the software delay */
|
||||
uint32_t spi_mode: 2; /*!< Traditional SPI mode (0 ~ 3) */
|
||||
uint32_t lcd_cmd_bytes: 3; /*!< Bytes of LCD command (1 ~ 4) */
|
||||
uint32_t lcd_param_bytes: 3; /*!< Bytes of LCD parameter (1 ~ 4) */
|
||||
struct {
|
||||
uint32_t use_dc_bit: 1; /*!< If this flag is enabled, transmit DC bit at the beginning of every command and data */
|
||||
uint32_t dc_zero_on_data: 1; /*!< If this flag is enabled, DC bit = 0 means transfer data, DC bit = 1 means transfer command */
|
||||
uint32_t lsb_first: 1; /*!< If this flag is enabled, transmit LSB bit first */
|
||||
uint32_t cs_high_active: 1; /*!< If this flag is enabled, CS line is high active */
|
||||
uint32_t del_keep_cs_inactive: 1; /*!< If this flag is enabled, keep CS line inactive even if panel_io is deleted */
|
||||
} flags;
|
||||
} esp_lcd_panel_io_3wire_spi_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new panel IO instance for 3-wire SPI interface (simulate by software)
|
||||
*
|
||||
* @note This function uses GPIO or IO expander to simulate SPI interface by software and just supports to write data.
|
||||
* It is only suitable for some applications with low speed SPI interface. (Such as initializing RGB panel)
|
||||
*
|
||||
* @param[in] io_config Panel IO configuration
|
||||
* @param[out] ret_io Pointer to return the created panel IO instance
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_NO_MEM: Failed to allocate memory for panel IO instance
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_io_3wire_spi(const esp_lcd_panel_io_3wire_spi_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(panel_io_3wire_spi_test)
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "test_esp_lcd_panel_io_additions.c")
|
||||
@@ -0,0 +1,7 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=4.4.2"
|
||||
espressif/esp_io_expander_tca9554: "^1.0.1"
|
||||
esp_lcd_panel_io_additions:
|
||||
version: "*"
|
||||
override_path: "../../"
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
|
||||
#include "esp_io_expander_tca9554.h"
|
||||
#include "esp_lcd_panel_io_additions.h"
|
||||
|
||||
static char *TAG = "panel_io_additions_test";
|
||||
|
||||
#define TEST_I2C_MASTER_NUM (I2C_NUM_0)
|
||||
#define TEST_I2C_CLK_SPEED (400 * 1000)
|
||||
#define TEST_I2C_SCL_GPIO_NUM (GPIO_NUM_18)
|
||||
#define TEST_I2C_SDA_GPIO_NUM (GPIO_NUM_8)
|
||||
|
||||
#define TEST_IO_EXPANDER_TC9554_ADDRESS (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000)
|
||||
|
||||
#define TEST_SPI_LINE_CS_GPIO_NUM (GPIO_NUM_10)
|
||||
#define TEST_SPI_LINE_SCL_GPIO_NUM (GPIO_NUM_11)
|
||||
#define TEST_SPI_LINE_SDA_GPIO_NUM (GPIO_NUM_12)
|
||||
#define TEST_SPI_LINE_CS_EX_PIN (IO_EXPANDER_PIN_NUM_1)
|
||||
#define TEST_SPI_LINE_SCL_EX_PIN (IO_EXPANDER_PIN_NUM_2)
|
||||
#define TEST_SPI_LINE_SDA_EX_PIN (IO_EXPANDER_PIN_NUM_3)
|
||||
|
||||
#define TEST_SET_SPI_LINE_GPIO(line) \
|
||||
do { \
|
||||
line.cs_io_type = IO_TYPE_GPIO; \
|
||||
line.cs_gpio_num = TEST_SPI_LINE_CS_GPIO_NUM; \
|
||||
line.scl_io_type = IO_TYPE_GPIO; \
|
||||
line.scl_gpio_num = TEST_SPI_LINE_SCL_GPIO_NUM; \
|
||||
line.sda_io_type = IO_TYPE_GPIO; \
|
||||
line.sda_gpio_num = TEST_SPI_LINE_SDA_GPIO_NUM; \
|
||||
line.io_expander = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define TEST_SET_SPI_LINE_EXPANDER(line, handle) \
|
||||
do { \
|
||||
line.cs_io_type = IO_TYPE_EXPANDER; \
|
||||
line.cs_expander_pin = TEST_SPI_LINE_CS_EX_PIN; \
|
||||
line.scl_io_type = IO_TYPE_EXPANDER; \
|
||||
line.scl_expander_pin = TEST_SPI_LINE_SCL_EX_PIN; \
|
||||
line.sda_io_type = IO_TYPE_EXPANDER; \
|
||||
line.sda_expander_pin = TEST_SPI_LINE_SDA_EX_PIN; \
|
||||
line.io_expander = handle; \
|
||||
} while (0)
|
||||
|
||||
typedef enum {
|
||||
SPI_LINE_TYPE_GPIO = 0,
|
||||
SPI_LINE_TYPE_EXPANDER,
|
||||
} test_3wire_spi_line_type_t;
|
||||
|
||||
static uint32_t test_cmd = 0x11223344;
|
||||
static uint8_t test_param[] = {0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};
|
||||
|
||||
static esp_lcd_panel_io_handle_t test_3wire_spi_common_init(test_3wire_spi_line_type_t line_type, esp_io_expander_handle_t ex_handle,
|
||||
bool use_dc_bit, int bytes_per_cmd_param)
|
||||
{
|
||||
spi_line_config_t spi_line;
|
||||
|
||||
if (line_type == SPI_LINE_TYPE_GPIO) {
|
||||
TEST_SET_SPI_LINE_GPIO(spi_line);
|
||||
} else {
|
||||
TEST_ASSERT(ex_handle != NULL);
|
||||
TEST_SET_SPI_LINE_EXPANDER(spi_line, ex_handle);
|
||||
}
|
||||
esp_lcd_panel_io_3wire_spi_config_t config = {
|
||||
.line_config = spi_line,
|
||||
.expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX,
|
||||
.spi_mode = 0,
|
||||
.lcd_cmd_bytes = bytes_per_cmd_param,
|
||||
.lcd_param_bytes = bytes_per_cmd_param,
|
||||
.flags = {
|
||||
.use_dc_bit = use_dc_bit,
|
||||
.dc_zero_on_data = 0,
|
||||
.lsb_first = 0,
|
||||
.cs_high_active = 0,
|
||||
.del_keep_cs_inactive = 0,
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_io_handle_t io = NULL;
|
||||
TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&config, &io));
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
TEST_CASE("test 3-wire spi use GPIO to write data", "[3wire_spi][gpio]")
|
||||
{
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
uint32_t cmd = 0;
|
||||
uint32_t param_1 = 0;
|
||||
uint32_t param_2 = 0;
|
||||
for (int i = 1; i <= sizeof(uint32_t); i++) {
|
||||
cmd = test_cmd & (0xffffffff >> (32 - 8 * i));
|
||||
param_1 = 0;
|
||||
param_2 = 0;
|
||||
int j = 0;
|
||||
for (; j < i; j++) {
|
||||
param_1 >>= 8;
|
||||
param_1 |= (uint32_t)test_param[j] << 24;
|
||||
param_2 >>= 8;
|
||||
param_2 |= (uint32_t)test_param[i + j] << 24;
|
||||
}
|
||||
for (; j < sizeof(uint32_t); j++) {
|
||||
param_1 >>= 8;
|
||||
param_2 >>= 8;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "test %d-bit command and parameters (no D/C bit)", 8 * i);
|
||||
ESP_LOGI(TAG, "send command[0x%"PRIx32"], parameters[0x%"PRIx32"][0x%"PRIx32"]", cmd, param_1, param_2);
|
||||
io_handle = NULL;
|
||||
io_handle = test_3wire_spi_common_init(SPI_LINE_TYPE_GPIO, NULL, false, i);
|
||||
TEST_ASSERT(io_handle != NULL);
|
||||
TEST_ESP_OK(esp_lcd_panel_io_tx_param(io_handle, test_cmd, test_param, i * 2));
|
||||
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
|
||||
|
||||
ESP_LOGI(TAG, "test %d-bit command and parameters (with D/C bit)", 8 * i + 1);
|
||||
ESP_LOGI(TAG, "send command[0x%"PRIx32"], parameters[0x1%"PRIx32"][0x1%"PRIx32"]", cmd, param_1, param_2);
|
||||
io_handle = NULL;
|
||||
io_handle = test_3wire_spi_common_init(SPI_LINE_TYPE_GPIO, NULL, true, i);
|
||||
TEST_ASSERT(io_handle != NULL);
|
||||
TEST_ESP_OK(esp_lcd_panel_io_tx_param(io_handle, test_cmd, test_param, i * 2));
|
||||
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("test 3-wire spi use IO expander to write data", "[3wire_spi][expander]")
|
||||
{
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = TEST_I2C_SDA_GPIO_NUM,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_io_num = TEST_I2C_SCL_GPIO_NUM,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.master.clk_speed = TEST_I2C_CLK_SPEED,
|
||||
};
|
||||
TEST_ESP_OK(i2c_param_config(TEST_I2C_MASTER_NUM, &i2c_conf));
|
||||
TEST_ESP_OK(i2c_driver_install(TEST_I2C_MASTER_NUM, i2c_conf.mode, 0, 0, 0));
|
||||
esp_io_expander_handle_t expander_handle = NULL;
|
||||
TEST_ESP_OK(esp_io_expander_new_i2c_tca9554(TEST_I2C_MASTER_NUM, TEST_IO_EXPANDER_TC9554_ADDRESS, &expander_handle));
|
||||
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
uint32_t cmd = 0;
|
||||
uint32_t param_1 = 0;
|
||||
uint32_t param_2 = 0;
|
||||
for (int i = 1; i <= sizeof(uint32_t); i++) {
|
||||
cmd = test_cmd & (0xffffffff >> (32 - 8 * i));
|
||||
param_1 = 0;
|
||||
param_2 = 0;
|
||||
int j = 0;
|
||||
for (; j < i; j++) {
|
||||
param_1 >>= 8;
|
||||
param_1 |= (uint32_t)test_param[j] << 24;
|
||||
param_2 >>= 8;
|
||||
param_2 |= (uint32_t)test_param[i + j] << 24;
|
||||
}
|
||||
for (; j < sizeof(uint32_t); j++) {
|
||||
param_1 >>= 8;
|
||||
param_2 >>= 8;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "test %d-bit command and parameters (no D/C bit)", 8 * i);
|
||||
ESP_LOGI(TAG, "send command[0x%"PRIx32"], parameters[0x%"PRIx32"][0x%"PRIx32"]", cmd, param_1, param_2);
|
||||
io_handle = NULL;
|
||||
io_handle = test_3wire_spi_common_init(SPI_LINE_TYPE_EXPANDER, expander_handle, false, i);
|
||||
TEST_ASSERT(io_handle != NULL);
|
||||
TEST_ESP_OK(esp_lcd_panel_io_tx_param(io_handle, test_cmd, test_param, i * 2));
|
||||
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
|
||||
|
||||
ESP_LOGI(TAG, "test %d-bit command and parameters (with D/C bit)", 8 * i + 1);
|
||||
ESP_LOGI(TAG, "send command[0x%"PRIx32"], parameters[0x1%"PRIx32"][0x1%"PRIx32"]", cmd, param_1, param_2);
|
||||
io_handle = NULL;
|
||||
io_handle = test_3wire_spi_common_init(SPI_LINE_TYPE_EXPANDER, expander_handle, true, i);
|
||||
TEST_ASSERT(io_handle != NULL);
|
||||
TEST_ESP_OK(esp_lcd_panel_io_tx_param(io_handle, test_cmd, test_param, i * 2));
|
||||
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_io_expander_del(expander_handle));
|
||||
TEST_ESP_OK(i2c_driver_delete(TEST_I2C_MASTER_NUM));
|
||||
}
|
||||
|
||||
// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ___ _ _____ ___ _ _ _ _ _ _ _ _
|
||||
// / _ \__ _ _ __ ___| | \_ \/___\ /_\ __| | __| (_) |_(_) ___ _ __ ___ | |_ ___ ___| |_
|
||||
// / /_)/ _` | '_ \ / _ \ | / /\// // //_\\ / _` |/ _` | | __| |/ _ \| '_ \/ __| | __/ _ \/ __| __|
|
||||
// / ___/ (_| | | | | __/ | /\/ /_/ \_// / _ \ (_| | (_| | | |_| | (_) | | | \__ \ | || __/\__ \ |_
|
||||
// \/ \__,_|_| |_|\___|_| \____/\___/ \_/ \_/\__,_|\__,_|_|\__|_|\___/|_| |_|___/ \__\___||___/\__|
|
||||
printf(" ___ _ _____ ___ _ _ _ _ _ _ _ _\r\n");
|
||||
printf(" / _ \\__ _ _ __ ___| | \\_ \\/___\\ /_\\ __| | __| (_) |_(_) ___ _ __ ___ | |_ ___ ___| |_\r\n");
|
||||
printf(" / /_)/ _` | '_ \\ / _ \\ | / /\\// // //_\\\\ / _` |/ _` | | __| |/ _ \\| '_ \\/ __| | __/ _ \\/ __| __|\r\n");
|
||||
printf("/ ___/ (_| | | | | __/ | /\\/ /_/ \\_// / _ \\ (_| | (_| | | |_| | (_) | | | \\__ \\ | || __/\\__ \\ |_\r\n");
|
||||
printf("\\/ \\__,_|_| |_|\\___|_| \\____/\\___/ \\_/ \\_/\\__,_|\\__,_|_|\\__|_|\\___/|_| |_|___/ \\__\\___||___/\\__|\r\n");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.env('esp32_s3_lcd_ev_board')
|
||||
def test_usb_stream(dut: Dut)-> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# For IDF 5.0
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
|
||||
|
||||
# For IDF4.4
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
Reference in New Issue
Block a user