// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. // // 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. #include "sdkconfig.h" #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "esp_heap_caps.h" #include "scr_interface_driver.h" #include "driver/gpio.h" static const char *TAG = "screen interface"; #define LCD_IFACE_CHECK(a, str, ret) if(!(a)) { \ ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ } /**--------------------- I2S interface driver ----------------------*/ typedef struct { i2s_lcd_handle_t i2s_lcd_handle; scr_interface_driver_t interface_drv; } interface_i2s_handle_t; static esp_err_t _i2s_lcd_write_data(void *handle, uint16_t data) { interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv); #ifndef CONFIG_IDF_TARGET_ESP32S3 return i2s_lcd_write_data(interface_i2s->i2s_lcd_handle, data); #else return ESP_ERR_NOT_SUPPORTED; #endif } static esp_err_t _i2s_lcd_write_cmd(void *handle, uint16_t cmd) { interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv); #ifndef CONFIG_IDF_TARGET_ESP32S3 return i2s_lcd_write_cmd(interface_i2s->i2s_lcd_handle, cmd); #else return ESP_ERR_NOT_SUPPORTED; #endif } static esp_err_t _i2s_lcd_write(void *handle, const uint8_t *data, uint32_t length) { interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv); #ifndef CONFIG_IDF_TARGET_ESP32S3 return i2s_lcd_write(interface_i2s->i2s_lcd_handle, data, length); #else return ESP_ERR_NOT_SUPPORTED; #endif } static esp_err_t _i2s_lcd_read(void *handle, uint8_t *data, uint32_t length) { #ifndef CONFIG_IDF_TARGET_ESP32S3 return ESP_ERR_NOT_SUPPORTED; #else return ESP_ERR_NOT_SUPPORTED; #endif } static esp_err_t _i2s_lcd_acquire(void *handle) { interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv); #ifndef CONFIG_IDF_TARGET_ESP32S3 return i2s_lcd_acquire(interface_i2s->i2s_lcd_handle); #else return ESP_ERR_NOT_SUPPORTED; #endif } static esp_err_t _i2s_lcd_release(void *handle) { interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv); #ifndef CONFIG_IDF_TARGET_ESP32S3 return i2s_lcd_release(interface_i2s->i2s_lcd_handle); #else return ESP_ERR_NOT_SUPPORTED; #endif } /**--------------------- I2C interface driver ----------------------*/ #define SSD1306_WRITE_CMD 0x00 #define SSD1306_WRITE_DAT 0x40 #define ACK_CHECK_EN 1 /*!< I2C master will check ack from slave*/ #define ACK_CHECK_DIS 0 /*!< I2C master will not check ack from slave */ typedef struct { i2c_bus_device_handle_t i2c_dev; scr_interface_driver_t interface_drv; } interface_i2c_handle_t; static esp_err_t i2c_lcd_driver_init(const scr_interface_i2c_config_t *cfg, interface_i2c_handle_t *out_interface_i2c) { i2c_bus_device_handle_t i2c_dev = i2c_bus_device_create(cfg->i2c_bus, cfg->slave_addr, cfg->clk_speed); LCD_IFACE_CHECK(NULL != i2c_dev, "I2C bus initial failed", ESP_FAIL); out_interface_i2c->i2c_dev = i2c_dev; return ESP_OK; } static esp_err_t i2c_lcd_driver_deinit(interface_i2c_handle_t *interface_i2c) { i2c_bus_device_delete(&interface_i2c->i2c_dev); return ESP_OK; } static esp_err_t i2c_lcd_write_byte(i2c_bus_device_handle_t i2c_dev, uint8_t ctrl, uint8_t data) { esp_err_t ret; uint8_t buffer[2]; buffer[0] = ctrl; buffer[1] = data; ret = i2c_bus_write_bytes(i2c_dev, NULL_I2C_MEM_ADDR, 2, buffer); if (ESP_OK != ret) { ESP_LOGE(TAG, "i2c send failed [%s]", esp_err_to_name(ret)); return ESP_FAIL; } return ESP_OK; } static esp_err_t i2c_lcd_write_cmd(void *handle, uint16_t cmd) { interface_i2c_handle_t *interface_i2c = __containerof(handle, interface_i2c_handle_t, interface_drv); uint8_t v = cmd; return i2c_lcd_write_byte(interface_i2c->i2c_dev, SSD1306_WRITE_CMD, v); } static esp_err_t i2c_lcd_write_data(void *handle, uint16_t data) { interface_i2c_handle_t *interface_i2c = __containerof(handle, interface_i2c_handle_t, interface_drv); uint8_t v = data; return i2c_lcd_write_byte(interface_i2c->i2c_dev, SSD1306_WRITE_DAT, v); } static esp_err_t i2c_lcd_write(void *handle, const uint8_t *data, uint32_t length) { interface_i2c_handle_t *interface_i2c = __containerof(handle, interface_i2c_handle_t, interface_drv); esp_err_t ret; ret = i2c_bus_write_bytes(interface_i2c->i2c_dev, SSD1306_WRITE_DAT, length, (uint8_t *)data); LCD_IFACE_CHECK(ESP_OK == ret, "i2C send failed", ESP_FAIL); return ESP_OK; } static esp_err_t i2c_lcd_read(void *handle, uint8_t *data, uint32_t length) { ESP_LOGW(TAG, "lcd i2c unsupport read"); return ESP_ERR_NOT_SUPPORTED; } static esp_err_t i2c_lcd_acquire(void *handle) { return ESP_ERR_NOT_SUPPORTED; } static esp_err_t i2c_lcd_release(void *handle) { return ESP_ERR_NOT_SUPPORTED; } /**--------------------- SPI interface driver ----------------------*/ #define LCD_CMD_LEV (0) #define LCD_DATA_LEV (1) typedef struct { spi_bus_device_handle_t spi_wr_dev; int8_t pin_num_dc; uint8_t swap_data; scr_interface_driver_t interface_drv; } interface_spi_handle_t; static esp_err_t spi_lcd_driver_init(const scr_interface_spi_config_t *cfg, interface_spi_handle_t *out_interface_spi) { LCD_IFACE_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(cfg->pin_num_cs), "gpio cs invalid", ESP_ERR_INVALID_ARG); LCD_IFACE_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(cfg->pin_num_dc), "gpio dc invalid", ESP_ERR_INVALID_ARG); //Initialize non-SPI GPIOs gpio_pad_select_gpio(cfg->pin_num_dc); gpio_set_direction(cfg->pin_num_dc, GPIO_MODE_OUTPUT); out_interface_spi->pin_num_dc = cfg->pin_num_dc; out_interface_spi->swap_data = cfg->swap_data; spi_device_config_t devcfg = { .clock_speed_hz = cfg->clk_freq, //Clock out frequency .mode = 0, //SPI mode 0 .cs_io_num = cfg->pin_num_cs, //CS pin }; out_interface_spi->spi_wr_dev = spi_bus_device_create(cfg->spi_bus, &devcfg); LCD_IFACE_CHECK(NULL != out_interface_spi->spi_wr_dev, "spi device initialize failed", ESP_FAIL); return ESP_OK; } static esp_err_t spi_lcd_driver_deinit(interface_spi_handle_t *interface_spi) { spi_bus_device_delete(&interface_spi->spi_wr_dev); return ESP_OK; } static esp_err_t spi_lcd_driver_acquire(void *handle) { return ESP_ERR_NOT_SUPPORTED; } static esp_err_t spi_lcd_driver_release(void *handle) { return ESP_ERR_NOT_SUPPORTED; } static esp_err_t _lcd_spi_rw(spi_bus_device_handle_t spi, const uint8_t *output, uint8_t *input, uint32_t length) { LCD_IFACE_CHECK(0 != length, "Length should not be 0", ESP_ERR_INVALID_ARG); return spi_bus_transfer_bytes(spi, output, input, length); } static esp_err_t spi_lcd_driver_write_cmd(void *handle, uint16_t value) { interface_spi_handle_t *interface_spi = __containerof(handle, interface_spi_handle_t, interface_drv); esp_err_t ret; gpio_set_level(interface_spi->pin_num_dc, LCD_CMD_LEV); uint8_t data = value; ret = _lcd_spi_rw(interface_spi->spi_wr_dev, &data, NULL, 1); gpio_set_level(interface_spi->pin_num_dc, LCD_DATA_LEV); LCD_IFACE_CHECK(ESP_OK == ret, "Send cmd failed", ESP_FAIL); return ESP_OK; } static esp_err_t spi_lcd_driver_write_data(void *handle, uint16_t value) { interface_spi_handle_t *interface_spi = __containerof(handle, interface_spi_handle_t, interface_drv); esp_err_t ret; uint8_t data = value; ret = _lcd_spi_rw(interface_spi->spi_wr_dev, &data, NULL, 1); LCD_IFACE_CHECK(ESP_OK == ret, "Send cmd failed", ESP_FAIL); return ESP_OK; } static esp_err_t spi_lcd_driver_read(void *handle, uint8_t *data, uint32_t length) { interface_spi_handle_t *interface_spi = __containerof(handle, interface_spi_handle_t, interface_drv); esp_err_t ret; ret = _lcd_spi_rw(interface_spi->spi_wr_dev, NULL, data, length); LCD_IFACE_CHECK(ESP_OK == ret, "Read data failed", ESP_FAIL); return ESP_OK; } static esp_err_t spi_lcd_driver_write(void *handle, const uint8_t *data, uint32_t length) { interface_spi_handle_t *interface_spi = __containerof(handle, interface_spi_handle_t, interface_drv); esp_err_t ret; /**< Swap the high and low byte of the data */ uint32_t l = length / 2; uint16_t t; if (interface_spi->swap_data) { uint16_t *p = (uint16_t *)data; for (size_t i = 0; i < l; i++) { t = *p; *p = t >> 8 | t << 8; p++; } } ret = _lcd_spi_rw(interface_spi->spi_wr_dev, data, NULL, length); /** * @brief swap data to restore the order of data * * TODO: how to avoid swap data here * */ if (interface_spi->swap_data) { uint16_t *_p = (uint16_t *)data; for (size_t i = 0; i < l; i++) { t = *_p; *_p = t >> 8 | t << 8; _p++; } } LCD_IFACE_CHECK(ESP_OK == ret, "Write data failed", ESP_FAIL); return ESP_OK; } /*********************************************************/ esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_interface_driver_t **out_driver) { LCD_IFACE_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG); LCD_IFACE_CHECK(NULL != out_driver, "Pointer of driver is invalid", ESP_ERR_INVALID_ARG); switch (type) { case SCREEN_IFACE_8080: { interface_i2s_handle_t *interface_i2s = heap_caps_malloc(sizeof(interface_i2s_handle_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); LCD_IFACE_CHECK(NULL != interface_i2s, "memory of iface i2s is not enough", ESP_ERR_NO_MEM); #ifndef CONFIG_IDF_TARGET_ESP32S3 interface_i2s->i2s_lcd_handle = i2s_lcd_driver_init((i2s_lcd_config_t *)config); #endif if (NULL == interface_i2s->i2s_lcd_handle) { ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "screen 8080 interface create failed"); heap_caps_free(interface_i2s); return ESP_FAIL; } interface_i2s->interface_drv.type = type; interface_i2s->interface_drv.write_cmd = _i2s_lcd_write_cmd; interface_i2s->interface_drv.write_data = _i2s_lcd_write_data; interface_i2s->interface_drv.write = _i2s_lcd_write; interface_i2s->interface_drv.read = _i2s_lcd_read; interface_i2s->interface_drv.bus_acquire = _i2s_lcd_acquire; interface_i2s->interface_drv.bus_release = _i2s_lcd_release; *out_driver = &interface_i2s->interface_drv; } break; case SCREEN_IFACE_SPI: { interface_spi_handle_t *interface_spi = heap_caps_malloc(sizeof(interface_spi_handle_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); LCD_IFACE_CHECK(NULL != interface_spi, "memory of iface spi is not enough", ESP_ERR_NO_MEM); esp_err_t ret = spi_lcd_driver_init((scr_interface_spi_config_t *)config, interface_spi); if (ESP_OK != ret) { ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "screen spi interface create failed"); heap_caps_free(interface_spi); return ESP_FAIL; } interface_spi->interface_drv.type = type; interface_spi->interface_drv.write_cmd = spi_lcd_driver_write_cmd; interface_spi->interface_drv.write_data = spi_lcd_driver_write_data; interface_spi->interface_drv.write = spi_lcd_driver_write; interface_spi->interface_drv.read = spi_lcd_driver_read; interface_spi->interface_drv.bus_acquire = spi_lcd_driver_acquire; interface_spi->interface_drv.bus_release = spi_lcd_driver_release; *out_driver = &interface_spi->interface_drv; } break; case SCREEN_IFACE_I2C: { interface_i2c_handle_t *interface_i2c = heap_caps_malloc(sizeof(interface_i2c_handle_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); LCD_IFACE_CHECK(NULL != interface_i2c, "memory of iface i2c is not enough", ESP_ERR_NO_MEM); esp_err_t ret = i2c_lcd_driver_init((scr_interface_i2c_config_t *)config, interface_i2c); if (ESP_OK != ret) { ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "screen i2c interface create failed"); heap_caps_free(interface_i2c); return ESP_FAIL; } interface_i2c->interface_drv.type = type; interface_i2c->interface_drv.write_cmd = i2c_lcd_write_cmd; interface_i2c->interface_drv.write_data = i2c_lcd_write_data; interface_i2c->interface_drv.write = i2c_lcd_write; interface_i2c->interface_drv.read = i2c_lcd_read; interface_i2c->interface_drv.bus_acquire = i2c_lcd_acquire; interface_i2c->interface_drv.bus_release = i2c_lcd_release; *out_driver = &interface_i2c->interface_drv; } break; default: break; } return ESP_OK; } esp_err_t scr_interface_delete(const scr_interface_driver_t *driver) { LCD_IFACE_CHECK(NULL != driver, "Pointer of driver is invalid", ESP_ERR_INVALID_ARG); switch (driver->type) { case SCREEN_IFACE_8080: { interface_i2s_handle_t *interface_i2s = __containerof(driver, interface_i2s_handle_t, interface_drv); i2s_lcd_driver_deinit(interface_i2s->i2s_lcd_handle); heap_caps_free(interface_i2s); } break; case SCREEN_IFACE_SPI: { interface_spi_handle_t *interface_spi = __containerof(driver, interface_spi_handle_t, interface_drv); spi_lcd_driver_deinit(interface_spi); heap_caps_free(interface_spi); } break; case SCREEN_IFACE_I2C: { interface_i2c_handle_t *interface_i2c = __containerof(driver, interface_i2c_handle_t, interface_drv); i2c_lcd_driver_deinit(interface_i2c); heap_caps_free(interface_i2c); } break; default: break; } return ESP_OK; }