403 lines
14 KiB
C
403 lines
14 KiB
C
// 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 <string.h>
|
|
#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;
|
|
}
|
|
|