chore: import local project into Gitea
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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,2 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "include" )
|
||||
@@ -0,0 +1,19 @@
|
||||
menu "Bus Options"
|
||||
|
||||
menu "I2C Bus Options"
|
||||
config I2C_BUS_DYNAMIC_CONFIG
|
||||
bool "enable dynamic configuration"
|
||||
default y
|
||||
help
|
||||
If enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
hence multiple devices with different configs on a single bus can be supported.
|
||||
|
||||
config I2C_MS_TO_WAIT
|
||||
int "mutex block time"
|
||||
default 200
|
||||
range 50 5000
|
||||
help
|
||||
task block time when try to take the bus, unit:milliseconds
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_SRCDIRS := .
|
||||
@@ -0,0 +1,489 @@
|
||||
#define CONFIG_I2C_BUS_DYNAMIC_CONFIG y
|
||||
#define CONFIG_I2C_MS_TO_WAIT 200
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "bus/include/i2c_bus.h"
|
||||
|
||||
#define I2C_ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define I2C_ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define I2C_BUS_FLG_DEFAULT (0)
|
||||
#define I2C_BUS_MASTER_BUF_LEN (0)
|
||||
#define I2C_BUS_MS_TO_WAIT CONFIG_I2C_MS_TO_WAIT
|
||||
#define I2C_BUS_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_RATE_MS)
|
||||
#define I2C_BUS_MUTEX_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_RATE_MS)
|
||||
|
||||
typedef struct {
|
||||
i2c_port_t i2c_port; /*!<I2C port number */
|
||||
bool is_init; /*if bus is initialized*/
|
||||
i2c_config_t conf_active; /*!<I2C active configuration */
|
||||
SemaphoreHandle_t mutex; /* mutex to achive thread-safe*/
|
||||
int32_t ref_counter; /*reference count*/
|
||||
} i2c_bus_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t dev_addr; /*device address*/
|
||||
i2c_config_t conf; /*!<I2C active configuration */
|
||||
i2c_bus_t *i2c_bus; /*!<I2C bus*/
|
||||
} i2c_bus_device_t;
|
||||
|
||||
static const char *TAG = "i2c_bus";
|
||||
static i2c_bus_t s_i2c_bus[I2C_NUM_MAX];
|
||||
|
||||
#define I2C_BUS_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_CHECK_GOTO(a, str, lable) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
goto lable; \
|
||||
}
|
||||
|
||||
#define I2C_BUS_INIT_CHECK(is_init, ret) if(!is_init) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):i2c_bus has not inited", __FILE__, __LINE__, __FUNCTION__); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE(mutex, ret) if (!xSemaphoreTake(mutex, I2C_BUS_MUTEX_TICKS_TO_WAIT)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %d ms", I2C_BUS_MUTEX_TICKS_TO_WAIT); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE_MAX_DELAY(mutex, ret) if (!xSemaphoreTake(mutex, portMAX_DELAY)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %d ms", portMAX_DELAY); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_GIVE(mutex, ret) if (!xSemaphoreGive(mutex)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus give mutex failed"); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf);
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port);
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf);
|
||||
/**************************************** Public Functions (Application level)*********************************************/
|
||||
|
||||
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "I2C port error", NULL);
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", NULL);
|
||||
I2C_BUS_CHECK(conf->mode == I2C_MODE_MASTER, "i2c_bus only supports master mode", NULL);
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
/**if i2c_bus has been inited and configs not changed, return the handle directly**/
|
||||
if (i2c_config_compare(port, conf)) {
|
||||
ESP_LOGW(TAG, "i2c%d has been inited, return handle directly, ref_counter=%d", port, s_i2c_bus[port].ref_counter);
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
} else {
|
||||
s_i2c_bus[port].mutex = xSemaphoreCreateMutex();
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].mutex != NULL, "i2c_bus xSemaphoreCreateMutex failed", NULL);
|
||||
s_i2c_bus[port].ref_counter = 0;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_reinit(port, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "init error", NULL);
|
||||
s_i2c_bus[port].conf_active = *conf;
|
||||
s_i2c_bus[port].i2c_port = port;
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus)
|
||||
{
|
||||
I2C_BUS_CHECK(p_bus != NULL && *p_bus != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)(*p_bus);
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, ESP_FAIL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
/** if ref_counter == 0, de-init the bus**/
|
||||
if ((i2c_bus->ref_counter) > 0) {
|
||||
ESP_LOGW(TAG, "i2c%d is also handled by others ref_counter=%u, won't be de-inited", i2c_bus->i2c_port, i2c_bus->ref_counter);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_deinit(i2c_bus->i2c_port);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "deinit error", ret);
|
||||
vSemaphoreDelete(i2c_bus->mutex);
|
||||
*p_bus = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Handle error", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
uint8_t device_count = 0;
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, 0);
|
||||
for (uint8_t dev_address = 1; dev_address < 127; dev_address++) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (dev_address << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "found i2c device address = 0x%02x", dev_address);
|
||||
if (buf != NULL && device_count < num) {
|
||||
*(buf + device_count) = dev_address;
|
||||
}
|
||||
device_count++;
|
||||
}
|
||||
|
||||
i2c_cmd_link_delete(cmd);
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, 0);
|
||||
return device_count;
|
||||
}
|
||||
|
||||
uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->conf_active.master.clk_speed;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->ref_counter;
|
||||
}
|
||||
|
||||
i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", NULL);
|
||||
I2C_BUS_CHECK(clk_speed <= 400000, "clk_speed must <= 400000", NULL);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, NULL);
|
||||
i2c_bus_device_t *i2c_device = calloc(1, sizeof(i2c_bus_device_t));
|
||||
I2C_BUS_CHECK(i2c_device != NULL, "calloc memory failed", NULL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, NULL);
|
||||
i2c_device->dev_addr = dev_addr;
|
||||
i2c_device->conf = i2c_bus->conf_active;
|
||||
|
||||
/*if clk_speed == 0, current active clock speed will be used, else set a specified value*/
|
||||
if (clk_speed != 0) {
|
||||
i2c_device->conf.master.clk_speed = clk_speed;
|
||||
}
|
||||
|
||||
i2c_device->i2c_bus = i2c_bus;
|
||||
i2c_bus->ref_counter++;
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, NULL);
|
||||
return (i2c_bus_device_handle_t)i2c_device;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(p_dev_handle != NULL && *p_dev_handle != NULL, "Null Device Handle", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)(*p_dev_handle);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_device->i2c_bus->ref_counter--;
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
free(i2c_device);
|
||||
*p_dev_handle = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", NULL_I2C_DEV_ADDR);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
return i2c_device->dev_addr;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, 1, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_reg8(dev_handle, mem_address, 1, &byte);
|
||||
*data = byte & (1 << bit_num);
|
||||
*data = (*data != 0) ? 1 : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
byte &= mask;
|
||||
byte >>= (bit_start - length + 1);
|
||||
*data = byte;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, 1, &data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
byte = (data != 0) ? (byte | (1 << bit_num)) : (byte & ~(1 << bit_num));
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
data <<= (bit_start - length + 1); // shift data into correct position
|
||||
data &= mask; // zero all non-important bits in data
|
||||
byte &= ~(mask); // zero all important bits in existing byte
|
||||
byte |= data; // combine data with existing byte
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C master send queued commands.
|
||||
* This function will trigger sending all queued commands.
|
||||
* The task will be blocked until all the commands have been sent out.
|
||||
* If I2C_BUS_DYNAMIC_CONFIG enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
* hence multiple devices with different configs on a single bus can be supported.
|
||||
* @note
|
||||
* Only call this function in I2C master mode
|
||||
*
|
||||
* @param i2c_num I2C port number
|
||||
* @param cmd_handle I2C command handler
|
||||
* @param ticks_to_wait maximum wait ticks.
|
||||
* @param conf pointer to I2C parameter settings
|
||||
* @return esp_err_t
|
||||
*/
|
||||
inline static esp_err_t i2c_master_cmd_begin_with_conf(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait, const i2c_config_t *conf)
|
||||
{
|
||||
esp_err_t ret;
|
||||
#ifdef CONFIG_I2C_BUS_DYNAMIC_CONFIG
|
||||
/*if configs changed, i2c driver will reinit with new configuration*/
|
||||
if (conf != NULL && false == i2c_config_compare(i2c_num, conf)) {
|
||||
ret = i2c_driver_reinit(i2c_num, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "reinit error", ret);
|
||||
s_i2c_bus[i2c_num].conf_active = *conf;
|
||||
}
|
||||
#endif
|
||||
ret = i2c_master_cmd_begin(i2c_num, cmd_handle, ticks_to_wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
esp_err_t i2c_bus_cmd_begin(i2c_bus_device_handle_t dev_handle, i2c_cmd_handle_t cmd)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(cmd != NULL, "I2C command error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, mem_address, I2C_ACK_CHECK_EN);
|
||||
}
|
||||
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_READ, I2C_ACK_CHECK_EN);
|
||||
i2c_master_read(cmd, data, data_len, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
uint8_t memAddress8[2];
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_write(cmd, memAddress8, 2, I2C_ACK_CHECK_EN);
|
||||
}
|
||||
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_READ, I2C_ACK_CHECK_EN);
|
||||
i2c_master_read(cmd, data, data_len, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
i2c_master_write_byte(cmd, mem_address, I2C_ACK_CHECK_EN);
|
||||
}
|
||||
|
||||
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
uint8_t memAddress8[2];
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
i2c_master_write(cmd, memAddress8, 2, I2C_ACK_CHECK_EN);
|
||||
}
|
||||
|
||||
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************** Private Functions*********************************************/
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
i2c_driver_delete(port);
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG, "i2c%d bus deinited", port);
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_param_config(port, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c param config failed", ret);
|
||||
ret = i2c_driver_install(port, conf->mode, I2C_BUS_MASTER_BUF_LEN, I2C_BUS_MASTER_BUF_LEN, I2C_BUS_FLG_DEFAULT);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c driver install failed", ret);
|
||||
s_i2c_bus[port].is_init = true;
|
||||
ESP_LOGI(TAG, "i2c%d bus inited", port);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port)
|
||||
{
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].is_init == true, "i2c not inited", ESP_ERR_INVALID_STATE);
|
||||
i2c_driver_delete(port); //always return ESP_OK
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG,"i2c%d bus deinited",port);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief compare with active i2c_bus configuration
|
||||
*
|
||||
* @param port choose which i2c_port's configuration will be compared
|
||||
* @param conf new configuration
|
||||
* @return true new configuration is equal to active configuration
|
||||
* @return false new configuration is not equal to active configuration
|
||||
*/
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
if (s_i2c_bus[port].conf_active.master.clk_speed == conf->master.clk_speed
|
||||
&& s_i2c_bus[port].conf_active.sda_io_num == conf->sda_io_num
|
||||
&& s_i2c_bus[port].conf_active.scl_io_num == conf->scl_io_num
|
||||
&& s_i2c_bus[port].conf_active.scl_pullup_en == conf->scl_pullup_en
|
||||
&& s_i2c_bus[port].conf_active.sda_pullup_en == conf->sda_pullup_en) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE 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"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp32/rom/lldesc.h"
|
||||
#include "soc/dport_access.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "esp_log.h"
|
||||
#include "bus/include/i2s_lcd_driver.h"
|
||||
|
||||
static const char *TAG = "ESP32_I2S_LCD";
|
||||
|
||||
#define I2S_CHECK(a, str, ret) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE (4000) // 4-byte aligned
|
||||
#define LCD_DATA_MAX_WIDTH (24) /*!< Maximum width of LCD data bus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t dma_buffer_size;
|
||||
uint32_t dma_half_buffer_size;
|
||||
uint32_t dma_node_buffer_size;
|
||||
uint32_t dma_node_cnt;
|
||||
uint32_t dma_half_node_cnt;
|
||||
lldesc_t *dma;
|
||||
uint8_t *dma_buffer;
|
||||
QueueHandle_t event_queue;
|
||||
uint8_t width;
|
||||
bool swap_data;
|
||||
intr_handle_t lcd_cam_intr_handle;
|
||||
i2s_dev_t *i2s_dev;
|
||||
} i2s_lcd_obj_t;
|
||||
|
||||
typedef struct {
|
||||
void (*i2s_write_data_func)(i2s_lcd_obj_t *i2s_lcd_obj, uint8_t *data, size_t len);
|
||||
int rs_io_num;
|
||||
i2s_lcd_obj_t *i2s_lcd_obj;
|
||||
SemaphoreHandle_t mutex;
|
||||
} i2s_lcd_driver_t;
|
||||
|
||||
static void IRAM_ATTR i2s_isr(void *arg)
|
||||
{
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
i2s_lcd_obj_t *i2s_lcd_obj = (i2s_lcd_obj_t *)arg;
|
||||
i2s_dev_t *i2s_dev = i2s_lcd_obj->i2s_dev;
|
||||
|
||||
typeof(i2s_dev->int_st) status = i2s_dev->int_st;
|
||||
i2s_dev->int_clr.val = status.val;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.out_eof) {
|
||||
xQueueSendFromISR(i2s_lcd_obj->event_queue, (void *)&status.val, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd_dma_set_int(i2s_lcd_obj_t *i2s_lcd_obj)
|
||||
{
|
||||
// Generate a data DMA linked list
|
||||
for (int x = 0; x < i2s_lcd_obj->dma_node_cnt; x++) {
|
||||
i2s_lcd_obj->dma[x].size = i2s_lcd_obj->dma_node_buffer_size;
|
||||
i2s_lcd_obj->dma[x].length = i2s_lcd_obj->dma_node_buffer_size;
|
||||
i2s_lcd_obj->dma[x].buf = (i2s_lcd_obj->dma_buffer + i2s_lcd_obj->dma_node_buffer_size * x);
|
||||
i2s_lcd_obj->dma[x].eof = !((x + 1) % i2s_lcd_obj->dma_half_node_cnt);
|
||||
i2s_lcd_obj->dma[x].empty = (uint32_t)&i2s_lcd_obj->dma[(x + 1) % i2s_lcd_obj->dma_node_cnt];
|
||||
}
|
||||
i2s_lcd_obj->dma[i2s_lcd_obj->dma_half_node_cnt - 1].empty = (uint32_t)NULL;
|
||||
i2s_lcd_obj->dma[i2s_lcd_obj->dma_node_cnt - 1].empty = (uint32_t)NULL;
|
||||
}
|
||||
|
||||
static void lcd_dma_set_left(i2s_lcd_obj_t *i2s_lcd_obj, int pos, size_t len)
|
||||
{
|
||||
int end_pos = 0, size = 0;
|
||||
// Processing data length is an integer multiple of i2s_lcd_obj->dma_node_buffer_size
|
||||
if (len % i2s_lcd_obj->dma_node_buffer_size) {
|
||||
end_pos = (pos % 2) * i2s_lcd_obj->dma_half_node_cnt + len / i2s_lcd_obj->dma_node_buffer_size;
|
||||
size = len % i2s_lcd_obj->dma_node_buffer_size;
|
||||
} else {
|
||||
end_pos = (pos % 2) * i2s_lcd_obj->dma_half_node_cnt + len / i2s_lcd_obj->dma_node_buffer_size - 1;
|
||||
size = i2s_lcd_obj->dma_node_buffer_size;
|
||||
}
|
||||
// Process the tail node to make it a DMA tail
|
||||
i2s_lcd_obj->dma[end_pos].size = size;
|
||||
i2s_lcd_obj->dma[end_pos].length = size;
|
||||
i2s_lcd_obj->dma[end_pos].eof = 1;
|
||||
i2s_lcd_obj->dma[end_pos].empty = (uint32_t)NULL;
|
||||
}
|
||||
|
||||
static void lcd_i2s_start(i2s_dev_t *i2s_dev, uint8_t fifo_mode, uint32_t addr, size_t len)
|
||||
{
|
||||
while (!i2s_dev->state.tx_idle);
|
||||
i2s_dev->fifo_conf.tx_fifo_mod = fifo_mode;
|
||||
i2s_dev->conf.tx_start = 0;
|
||||
i2s_dev->conf.tx_reset = 1;
|
||||
i2s_dev->conf.tx_reset = 0;
|
||||
i2s_dev->lc_conf.out_rst = 1;
|
||||
i2s_dev->lc_conf.out_rst = 0;
|
||||
i2s_dev->conf.tx_fifo_reset = 1;
|
||||
i2s_dev->conf.tx_fifo_reset = 0;
|
||||
i2s_dev->out_link.addr = addr;
|
||||
i2s_dev->out_link.start = 1;
|
||||
ets_delay_us(1);
|
||||
i2s_dev->conf.tx_start = 1;
|
||||
}
|
||||
|
||||
static void i2s_write_8bit_data(i2s_lcd_obj_t *i2s_lcd_obj, uint8_t *data, size_t len)
|
||||
{
|
||||
int event = 0;
|
||||
int x = 0, y = 0, left = 0, cnt = 0;
|
||||
if (len <= 0) {
|
||||
ESP_LOGE(TAG, "wrong len!");
|
||||
return;
|
||||
}
|
||||
len = len * 2;
|
||||
lcd_dma_set_int(i2s_lcd_obj);
|
||||
uint8_t fifo_mode = 1;
|
||||
// Start signal
|
||||
xQueueSend(i2s_lcd_obj->event_queue, &event, 0);
|
||||
cnt = len / i2s_lcd_obj->dma_half_buffer_size;
|
||||
// Process a complete piece of data, ping-pong operation
|
||||
for (x = 0; x < cnt; x++) {
|
||||
uint8_t *out = (uint8_t *)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
if (!i2s_lcd_obj->swap_data) { // data will be swapped when fifo_mode=1, so negate the lcd.swap_data
|
||||
for (y = 0; y < i2s_lcd_obj->dma_half_buffer_size; y += 4) {
|
||||
out[y + 3] = in[(y >> 1) + 0];
|
||||
out[y + 1] = in[(y >> 1) + 1];
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < i2s_lcd_obj->dma_half_buffer_size; y += 4) {
|
||||
out[y + 1] = in[(y >> 1) + 0];
|
||||
out[y + 3] = in[(y >> 1) + 1];
|
||||
}
|
||||
}
|
||||
data += i2s_lcd_obj->dma_half_buffer_size >> 1;
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, fifo_mode, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, i2s_lcd_obj->dma_half_buffer_size);
|
||||
}
|
||||
left = len % i2s_lcd_obj->dma_half_buffer_size;
|
||||
// Process remaining incomplete segment data
|
||||
while (left) {
|
||||
uint8_t *out = (uint8_t *)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
if (left > 2) {
|
||||
cnt = left - left % 4;
|
||||
left = left % 4;
|
||||
data += cnt >> 1;
|
||||
if (!i2s_lcd_obj->swap_data) { // data will be swapped when fifo_mode=1, so negate the lcd.swap_data
|
||||
for (y = 0; y < cnt; y += 4) {
|
||||
out[y + 3] = in[(y >> 1) + 0];
|
||||
out[y + 1] = in[(y >> 1) + 1];
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < cnt; y += 4) {
|
||||
out[y + 1] = in[(y >> 1) + 0];
|
||||
out[y + 3] = in[(y >> 1) + 1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cnt = 4;
|
||||
left = 0;
|
||||
fifo_mode = 3;
|
||||
out[3] = in[0];
|
||||
}
|
||||
// printf("[");
|
||||
// for (size_t i = 0; i < cnt; i++) {
|
||||
// printf("%02x, ", out[i]);
|
||||
// } printf("]\n");
|
||||
lcd_dma_set_left(i2s_lcd_obj, x, cnt);
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, fifo_mode, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, cnt);
|
||||
x++;
|
||||
}
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void i2s_write_16bit_data(i2s_lcd_obj_t *i2s_lcd_obj, uint8_t *data, size_t len)
|
||||
{
|
||||
int event = 0;
|
||||
int x = 0, y = 0, left = 0, cnt = 0;
|
||||
if (len <= 0 || len % 2 != 0) {
|
||||
ESP_LOGE(TAG, "wrong len!");
|
||||
return;
|
||||
}
|
||||
lcd_dma_set_int(i2s_lcd_obj);
|
||||
uint8_t fifo_mode = 1;
|
||||
// Start signal
|
||||
xQueueSend(i2s_lcd_obj->event_queue, &event, 0);
|
||||
cnt = len / i2s_lcd_obj->dma_half_buffer_size;
|
||||
// Process a complete piece of data, ping-pong operation
|
||||
for (x = 0; x < cnt; x++) {
|
||||
uint8_t *out = (uint8_t *)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
if (i2s_lcd_obj->swap_data) {
|
||||
for (y = 0; y < i2s_lcd_obj->dma_half_buffer_size; y += 4) {
|
||||
out[y + 3] = in[y + 0];
|
||||
out[y + 2] = in[y + 1];
|
||||
out[y + 1] = in[y + 2];
|
||||
out[y + 0] = in[y + 3];
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < i2s_lcd_obj->dma_half_buffer_size; y += 4) {
|
||||
out[y + 2] = in[y + 0];
|
||||
out[y + 3] = in[y + 1];
|
||||
out[y + 0] = in[y + 2];
|
||||
out[y + 1] = in[y + 3];
|
||||
}
|
||||
}
|
||||
data += i2s_lcd_obj->dma_half_buffer_size;
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, fifo_mode, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, i2s_lcd_obj->dma_half_buffer_size);
|
||||
}
|
||||
left = len % i2s_lcd_obj->dma_half_buffer_size;
|
||||
// Process remaining incomplete segment data
|
||||
while (left) {
|
||||
uint8_t *out = (uint8_t *)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
if (left > 2) {
|
||||
cnt = left - left % 4;
|
||||
left = left % 4;
|
||||
data += cnt;
|
||||
if (i2s_lcd_obj->swap_data) {
|
||||
for (y = 0; y < cnt; y += 4) {
|
||||
out[y + 3] = in[y + 0];
|
||||
out[y + 2] = in[y + 1];
|
||||
out[y + 1] = in[y + 2];
|
||||
out[y + 0] = in[y + 3];
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < cnt; y += 4) {
|
||||
out[y + 2] = in[y + 0];
|
||||
out[y + 3] = in[y + 1];
|
||||
out[y + 0] = in[y + 2];
|
||||
out[y + 1] = in[y + 3];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cnt = 4;
|
||||
left = 0;
|
||||
fifo_mode = 3;
|
||||
if (i2s_lcd_obj->swap_data) {
|
||||
out[3] = in[0];
|
||||
out[2] = in[1];
|
||||
} else {
|
||||
out[2] = in[0];
|
||||
out[3] = in[1];
|
||||
}
|
||||
}
|
||||
lcd_dma_set_left(i2s_lcd_obj, x, cnt);
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, fifo_mode, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, cnt);
|
||||
x++;
|
||||
}
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static esp_err_t i2s_lcd_reg_config(i2s_dev_t *i2s_dev, uint16_t data_width, uint32_t clk_freq)
|
||||
{
|
||||
// Configure the clock
|
||||
i2s_dev->clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz
|
||||
i2s_dev->clkm_conf.clkm_div_b = 0;
|
||||
i2s_dev->clkm_conf.clkm_div_a = 10;
|
||||
i2s_dev->clkm_conf.clk_en = 1;
|
||||
|
||||
i2s_dev->conf.val = 0;
|
||||
i2s_dev->fifo_conf.val = 0;
|
||||
i2s_dev->fifo_conf.dscr_en = 1;
|
||||
|
||||
i2s_dev->conf2.lcd_en = 1;
|
||||
i2s_dev->conf2.camera_en = 1;
|
||||
|
||||
i2s_dev->lc_conf.ahbm_fifo_rst = 1;
|
||||
i2s_dev->lc_conf.ahbm_fifo_rst = 0;
|
||||
i2s_dev->lc_conf.ahbm_rst = 1;
|
||||
i2s_dev->lc_conf.ahbm_rst = 0;
|
||||
i2s_dev->lc_conf.check_owner = 0;
|
||||
i2s_dev->lc_conf.out_loop_test = 0;
|
||||
i2s_dev->lc_conf.out_auto_wrback = 0;
|
||||
i2s_dev->lc_conf.out_data_burst_en = 1;
|
||||
i2s_dev->lc_conf.out_no_restart_clr = 0;
|
||||
i2s_dev->lc_conf.indscr_burst_en = 0;
|
||||
i2s_dev->lc_conf.out_eof_mode = 1;
|
||||
|
||||
i2s_dev->timing.val = 0;
|
||||
|
||||
i2s_dev->int_ena.val = 0;
|
||||
i2s_dev->int_clr.val = ~0;
|
||||
|
||||
// Configure sampling rate
|
||||
i2s_dev->sample_rate_conf.tx_bck_div_num = 40000000 / clk_freq; // Fws = Fbck / 2
|
||||
i2s_dev->sample_rate_conf.tx_bits_mod = (data_width == 8) ? 0 : 1;
|
||||
// Configuration data format
|
||||
i2s_dev->conf.tx_start = 0;
|
||||
i2s_dev->conf.tx_reset = 1;
|
||||
i2s_dev->conf.tx_reset = 0;
|
||||
i2s_dev->conf.tx_fifo_reset = 1;
|
||||
i2s_dev->conf.tx_fifo_reset = 0;
|
||||
i2s_dev->conf.tx_slave_mod = 0;
|
||||
i2s_dev->conf.tx_right_first = 1; // Must be set to 1, otherwise the clock line will change during reset
|
||||
i2s_dev->conf.tx_msb_right = 0;
|
||||
i2s_dev->conf.tx_short_sync = 0;
|
||||
i2s_dev->conf.tx_mono = 0;
|
||||
i2s_dev->conf.tx_msb_shift = 0;
|
||||
|
||||
i2s_dev->conf1.tx_pcm_bypass = 1;
|
||||
i2s_dev->conf1.tx_stop_en = 1;
|
||||
|
||||
i2s_dev->conf_chan.tx_chan_mod = 1;
|
||||
|
||||
i2s_dev->fifo_conf.tx_fifo_mod_force_en = 1;
|
||||
i2s_dev->fifo_conf.tx_data_num = 32;
|
||||
i2s_dev->fifo_conf.tx_fifo_mod = 1;
|
||||
|
||||
i2s_dev->lc_conf.out_rst = 1;
|
||||
i2s_dev->lc_conf.out_rst = 0;
|
||||
|
||||
i2s_dev->int_ena.out_eof = 1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_set_pin(const i2s_lcd_config_t *config)
|
||||
{
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_num_wr], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_num_wr, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_num_wr, GPIO_FLOATING);
|
||||
gpio_matrix_out(config->pin_num_wr, I2S0O_WS_OUT_IDX, true, false);
|
||||
|
||||
for (int i = 0; i < config->data_width; i++) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_data_num[i]], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_data_num[i], GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_data_num[i], GPIO_FLOATING);
|
||||
// High bit aligned, OUT23 is always the highest bit
|
||||
gpio_matrix_out(config->pin_data_num[i], I2S0O_DATA_OUT0_IDX + (LCD_DATA_MAX_WIDTH - config->data_width) + i, false, false);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_dma_config(i2s_lcd_obj_t *i2s_lcd_obj, uint32_t max_dma_buffer_size)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE % 2 != 0) {
|
||||
ESP_LOGE(TAG, "ESP32 only supports 2-byte aligned data length");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (max_dma_buffer_size >= LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE * 2) {
|
||||
i2s_lcd_obj->dma_node_buffer_size = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE;
|
||||
for (cnt = 0; cnt < max_dma_buffer_size - 8; cnt++) { // Find a buffer size that can divide dma_size
|
||||
if ((max_dma_buffer_size - cnt) % (i2s_lcd_obj->dma_node_buffer_size * 2) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i2s_lcd_obj->dma_buffer_size = max_dma_buffer_size - cnt;
|
||||
} else {
|
||||
i2s_lcd_obj->dma_node_buffer_size = max_dma_buffer_size / 2;
|
||||
i2s_lcd_obj->dma_buffer_size = i2s_lcd_obj->dma_node_buffer_size * 2;
|
||||
}
|
||||
|
||||
i2s_lcd_obj->dma_half_buffer_size = i2s_lcd_obj->dma_buffer_size / 2;
|
||||
i2s_lcd_obj->dma_node_cnt = (i2s_lcd_obj->dma_buffer_size) / i2s_lcd_obj->dma_node_buffer_size; // Number of DMA nodes
|
||||
i2s_lcd_obj->dma_half_node_cnt = i2s_lcd_obj->dma_node_cnt / 2;
|
||||
|
||||
ESP_LOGI(TAG, "lcd_buffer_size: %d, lcd_dma_size: %d, lcd_dma_node_cnt: %d", i2s_lcd_obj->dma_buffer_size, i2s_lcd_obj->dma_node_buffer_size, i2s_lcd_obj->dma_node_cnt);
|
||||
|
||||
i2s_lcd_obj->dma = (lldesc_t *)heap_caps_calloc(i2s_lcd_obj->dma_node_cnt, sizeof(lldesc_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
i2s_lcd_obj->dma_buffer = (uint8_t *)heap_caps_calloc(i2s_lcd_obj->dma_buffer_size, sizeof(uint8_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_cam_deinit(i2s_lcd_driver_t *drv)
|
||||
{
|
||||
if (!drv->i2s_lcd_obj) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (drv->i2s_lcd_obj->event_queue) {
|
||||
vQueueDelete(drv->i2s_lcd_obj->event_queue);
|
||||
}
|
||||
if (drv->i2s_lcd_obj->dma) {
|
||||
heap_caps_free(drv->i2s_lcd_obj->dma);
|
||||
}
|
||||
if (drv->i2s_lcd_obj->dma_buffer) {
|
||||
heap_caps_free(drv->i2s_lcd_obj->dma_buffer);
|
||||
}
|
||||
|
||||
if (drv->i2s_lcd_obj->lcd_cam_intr_handle) {
|
||||
esp_intr_free(drv->i2s_lcd_obj->lcd_cam_intr_handle);
|
||||
}
|
||||
|
||||
heap_caps_free(drv->i2s_lcd_obj);
|
||||
drv->i2s_lcd_obj = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_cam_init(i2s_lcd_driver_t *drv, const i2s_lcd_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
i2s_lcd_obj_t *i2s_lcd_obj = (i2s_lcd_obj_t *)heap_caps_calloc(1, sizeof(i2s_lcd_obj_t), MALLOC_CAP_DMA);
|
||||
if (i2s_lcd_obj == NULL) {
|
||||
ESP_LOGE(TAG, "lcd_cam object malloc failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
drv->i2s_lcd_obj = i2s_lcd_obj;
|
||||
|
||||
if (I2S_NUM_0 == config->i2s_port) {
|
||||
i2s_lcd_obj->i2s_dev = &I2S0;
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
ESP_LOGI(TAG, "Enable I2S0");
|
||||
} else if (I2S_NUM_1 == config->i2s_port) {
|
||||
i2s_lcd_obj->i2s_dev = &I2S1;
|
||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||
ESP_LOGI(TAG, "Enable I2S1");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Designated I2S peripheral not found");
|
||||
}
|
||||
|
||||
do {
|
||||
ret |= i2s_lcd_reg_config(i2s_lcd_obj->i2s_dev, config->data_width, config->clk_freq);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd_cam config fail!");
|
||||
break;
|
||||
}
|
||||
|
||||
ret |= lcd_set_pin(config);
|
||||
ret |= lcd_dma_config(i2s_lcd_obj, config->buffer_size);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd config fail!");
|
||||
break;
|
||||
}
|
||||
|
||||
i2s_lcd_obj->event_queue = xQueueCreate(1, sizeof(int));
|
||||
i2s_lcd_obj->width = config->data_width;
|
||||
i2s_lcd_obj->swap_data = config->swap_data;;
|
||||
|
||||
if (i2s_lcd_obj->event_queue == NULL) {
|
||||
ESP_LOGE(TAG, "lcd config fail!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (I2S_NUM_0 == config->i2s_port) {
|
||||
ret |= esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, i2s_isr, i2s_lcd_obj, &i2s_lcd_obj->lcd_cam_intr_handle);
|
||||
} else if (I2S_NUM_1 == config->i2s_port) {
|
||||
ret |= esp_intr_alloc(ETS_I2S1_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, i2s_isr, i2s_lcd_obj, &i2s_lcd_obj->lcd_cam_intr_handle);
|
||||
}
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd_cam intr alloc fail!");
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "i2s lcd driver init ok");
|
||||
return ESP_OK;
|
||||
} while (0);
|
||||
|
||||
lcd_cam_deinit(drv);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/**< Public functions */
|
||||
|
||||
i2s_lcd_handle_t i2s_lcd_driver_init(const i2s_lcd_config_t *config)
|
||||
{
|
||||
I2S_CHECK(NULL != config, "config pointer invalid", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(config->pin_num_wr), "GPIO WR invalid", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(config->pin_num_rs), "GPIO RS invalid", NULL);
|
||||
I2S_CHECK(config->data_width > 0 && config->data_width <= 16, "Bit width out of range", NULL);
|
||||
I2S_CHECK(0 == (config->data_width % 8), "Bit width must be a multiple of 8", NULL);
|
||||
uint64_t pin_mask = 0;
|
||||
for (size_t i = 0; i < config->data_width; i++) {
|
||||
uint64_t mask = 1ULL << config->pin_data_num[i];
|
||||
I2S_CHECK(!(pin_mask & mask), "Data bus GPIO has a duplicate", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(config->pin_data_num[i]), "Data bus gpio invalid", NULL);
|
||||
pin_mask |= mask;
|
||||
}
|
||||
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)heap_caps_malloc(sizeof(i2s_lcd_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "Error malloc handle of i2s lcd driver", NULL);
|
||||
|
||||
esp_err_t ret = lcd_cam_init(i2s_lcd_drv, config);
|
||||
if (ESP_OK != ret) {
|
||||
ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "i2s lcd driver initialize failed");
|
||||
heap_caps_free(i2s_lcd_drv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i2s_lcd_drv->mutex = xSemaphoreCreateMutex();
|
||||
if (i2s_lcd_drv->mutex == NULL) {
|
||||
ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "lcd create mutex failed");
|
||||
lcd_cam_deinit(i2s_lcd_drv);
|
||||
heap_caps_free(i2s_lcd_drv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (8 == config->data_width) {
|
||||
i2s_lcd_drv->i2s_write_data_func = i2s_write_8bit_data;
|
||||
} else if (16 == config->data_width) {
|
||||
i2s_lcd_drv->i2s_write_data_func = i2s_write_16bit_data;
|
||||
}
|
||||
|
||||
if (config->pin_num_cs >= 0) {
|
||||
gpio_pad_select_gpio(config->pin_num_cs);
|
||||
gpio_set_direction(config->pin_num_cs, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(config->pin_num_cs, 0);
|
||||
}
|
||||
|
||||
gpio_pad_select_gpio(config->pin_num_rs);
|
||||
gpio_set_direction(config->pin_num_rs, GPIO_MODE_OUTPUT);
|
||||
i2s_lcd_drv->rs_io_num = config->pin_num_rs;
|
||||
return (i2s_lcd_handle_t)i2s_lcd_drv;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_driver_deinit(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
lcd_cam_deinit(i2s_lcd_drv);
|
||||
vSemaphoreDelete(i2s_lcd_drv->mutex);
|
||||
heap_caps_free(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write_data(i2s_lcd_handle_t handle, uint16_t data)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
i2s_lcd_drv->i2s_write_data_func(i2s_lcd_drv->i2s_lcd_obj, (uint8_t *)&data, i2s_lcd_drv->i2s_lcd_obj->width == 16 ? 2 : 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write_cmd(i2s_lcd_handle_t handle, uint16_t cmd)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
gpio_set_level(i2s_lcd_drv->rs_io_num, LCD_CMD_LEV);
|
||||
i2s_lcd_drv->i2s_write_data_func(i2s_lcd_drv->i2s_lcd_obj, (uint8_t *)&cmd, i2s_lcd_drv->i2s_lcd_obj->width == 16 ? 2 : 1);
|
||||
gpio_set_level(i2s_lcd_drv->rs_io_num, LCD_DATA_LEV);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write(i2s_lcd_handle_t handle, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
i2s_lcd_drv->i2s_write_data_func(i2s_lcd_drv->i2s_lcd_obj, (uint8_t *)data, length);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_acquire(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
BaseType_t ret = xSemaphoreTake(i2s_lcd_drv->mutex, portMAX_DELAY);
|
||||
I2S_CHECK(pdTRUE == ret, "Take semaphore failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_release(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
BaseType_t ret = xSemaphoreGive(i2s_lcd_drv->mutex);
|
||||
I2S_CHECK(pdTRUE == ret, "Give semaphore failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
@@ -0,0 +1,469 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE 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"
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp32s2/rom/lldesc.h"
|
||||
#include "soc/system_reg.h"
|
||||
#include "bus/include/i2s_lcd_driver.h"
|
||||
|
||||
|
||||
static const char *TAG = "ESP32S2_I2S_LCD";
|
||||
|
||||
#define I2S_CHECK(a, str, ret) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE (4000) // 4-byte aligned
|
||||
#define LCD_DATA_MAX_WIDTH (24) /*!< Maximum width of LCD data bus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t dma_buffer_size;
|
||||
uint32_t dma_half_buffer_size;
|
||||
uint32_t dma_node_buffer_size;
|
||||
uint32_t dma_node_cnt;
|
||||
uint32_t dma_half_node_cnt;
|
||||
lldesc_t *dma;
|
||||
uint8_t *dma_buffer;
|
||||
QueueHandle_t event_queue;
|
||||
uint8_t width;
|
||||
bool swap_data;
|
||||
intr_handle_t lcd_cam_intr_handle;
|
||||
i2s_dev_t *i2s_dev;
|
||||
} i2s_lcd_obj_t;
|
||||
|
||||
typedef struct {
|
||||
int rs_io_num;
|
||||
i2s_lcd_obj_t *i2s_lcd_obj;
|
||||
SemaphoreHandle_t mutex;
|
||||
} i2s_lcd_driver_t;
|
||||
|
||||
static void IRAM_ATTR i2s_isr(void *arg)
|
||||
{
|
||||
BaseType_t HPTaskAwoken = pdFALSE;
|
||||
i2s_lcd_obj_t *i2s_lcd_obj = (i2s_lcd_obj_t*)arg;
|
||||
i2s_dev_t *i2s_dev = i2s_lcd_obj->i2s_dev;
|
||||
|
||||
typeof(i2s_dev->int_st) status = i2s_dev->int_st;
|
||||
i2s_dev->int_clr.val = status.val;
|
||||
if (status.val == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.out_eof) {
|
||||
xQueueSendFromISR(i2s_lcd_obj->event_queue, (void*)&status.val, &HPTaskAwoken);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd_dma_set_int(i2s_lcd_obj_t *i2s_lcd_obj)
|
||||
{
|
||||
// Generate a data DMA linked list
|
||||
for (int x = 0; x < i2s_lcd_obj->dma_node_cnt; x++) {
|
||||
i2s_lcd_obj->dma[x].size = i2s_lcd_obj->dma_node_buffer_size;
|
||||
i2s_lcd_obj->dma[x].length = i2s_lcd_obj->dma_node_buffer_size;
|
||||
i2s_lcd_obj->dma[x].buf = (i2s_lcd_obj->dma_buffer + i2s_lcd_obj->dma_node_buffer_size * x);
|
||||
i2s_lcd_obj->dma[x].eof = !((x + 1) % i2s_lcd_obj->dma_half_node_cnt);
|
||||
i2s_lcd_obj->dma[x].empty = (uint32_t)&i2s_lcd_obj->dma[(x + 1) % i2s_lcd_obj->dma_node_cnt];
|
||||
}
|
||||
i2s_lcd_obj->dma[i2s_lcd_obj->dma_half_node_cnt - 1].empty = (uint32_t)NULL;
|
||||
i2s_lcd_obj->dma[i2s_lcd_obj->dma_node_cnt - 1].empty = (uint32_t)NULL;
|
||||
}
|
||||
|
||||
static void lcd_dma_set_left(i2s_lcd_obj_t *i2s_lcd_obj, int pos, size_t len)
|
||||
{
|
||||
int end_pos = 0, size = 0;
|
||||
// Processing data length is an integer multiple of i2s_lcd_obj->dma_node_buffer_size
|
||||
if (len % i2s_lcd_obj->dma_node_buffer_size) {
|
||||
end_pos = (pos % 2) * i2s_lcd_obj->dma_half_node_cnt + len / i2s_lcd_obj->dma_node_buffer_size;
|
||||
size = len % i2s_lcd_obj->dma_node_buffer_size;
|
||||
} else {
|
||||
end_pos = (pos % 2) * i2s_lcd_obj->dma_half_node_cnt + len / i2s_lcd_obj->dma_node_buffer_size - 1;
|
||||
size = i2s_lcd_obj->dma_node_buffer_size;
|
||||
}
|
||||
// Process the tail node to make it a DMA tail
|
||||
i2s_lcd_obj->dma[end_pos].size = size;
|
||||
i2s_lcd_obj->dma[end_pos].length = size;
|
||||
i2s_lcd_obj->dma[end_pos].eof = 1;
|
||||
i2s_lcd_obj->dma[end_pos].empty = (uint32_t)NULL;
|
||||
}
|
||||
|
||||
static void lcd_i2s_start(i2s_dev_t *i2s_dev, uint32_t addr, size_t len)
|
||||
{
|
||||
while (!i2s_dev->state.tx_idle);
|
||||
i2s_dev->conf.tx_start = 0;
|
||||
i2s_dev->conf.tx_reset = 1;
|
||||
i2s_dev->conf.tx_reset = 0;
|
||||
i2s_dev->conf.tx_fifo_reset = 1;
|
||||
i2s_dev->conf.tx_fifo_reset = 0;
|
||||
i2s_dev->out_link.addr = addr;
|
||||
i2s_dev->out_link.start = 1;
|
||||
ets_delay_us(1);
|
||||
i2s_dev->conf.tx_start = 1;
|
||||
}
|
||||
|
||||
static void i2s_write_data(i2s_lcd_obj_t *i2s_lcd_obj, uint8_t *data, size_t len)
|
||||
{
|
||||
int event = 0;
|
||||
int x = 0, y = 0, left = 0, cnt = 0;
|
||||
if (len <= 0) {
|
||||
ESP_LOGE(TAG, "wrong len!");
|
||||
return;
|
||||
}
|
||||
lcd_dma_set_int(i2s_lcd_obj);
|
||||
uint32_t half_buffer_size = i2s_lcd_obj->dma_half_buffer_size;
|
||||
cnt = len / half_buffer_size;
|
||||
// Start signal
|
||||
xQueueSend(i2s_lcd_obj->event_queue, &event, 0);
|
||||
// Process a complete piece of data, ping-pong operation
|
||||
for (x = 0; x < cnt; x++) {
|
||||
uint8_t *out = (uint8_t*)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
if (i2s_lcd_obj->swap_data) {
|
||||
uint8_t *out1 = out + 1;
|
||||
uint8_t *in1 = in + 1;
|
||||
for (y = 0; y < half_buffer_size;) {
|
||||
out1[y] = in[y];
|
||||
out[y] = in1[y];
|
||||
y += 2;
|
||||
out1[y] = in[y];
|
||||
out[y] = in1[y];
|
||||
y += 2;
|
||||
}
|
||||
} else {
|
||||
memcpy(out, in, half_buffer_size);
|
||||
}
|
||||
data += half_buffer_size;
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, half_buffer_size);
|
||||
}
|
||||
left = len % half_buffer_size;
|
||||
// Process remaining incomplete segment data
|
||||
if (left) {
|
||||
uint8_t *out = (uint8_t*)i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt].buf;
|
||||
uint8_t *in = data;
|
||||
cnt = left - left % 2;
|
||||
if (cnt) {
|
||||
if (i2s_lcd_obj->swap_data) {
|
||||
for (y = 0; y < cnt; y+=2) {
|
||||
out[y+1] = in[y+0];
|
||||
out[y+0] = in[y+1];
|
||||
}
|
||||
} else {
|
||||
memcpy(out, in, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
if (left % 2) {
|
||||
out[cnt] = in[cnt];
|
||||
}
|
||||
lcd_dma_set_left(i2s_lcd_obj, x, left);
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
lcd_i2s_start(i2s_lcd_obj->i2s_dev, ((uint32_t)&i2s_lcd_obj->dma[(x % 2) * i2s_lcd_obj->dma_half_node_cnt]) & 0xfffff, left);
|
||||
}
|
||||
xQueueReceive(i2s_lcd_obj->event_queue, (void *)&event, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static esp_err_t i2s_lcd_reg_config(i2s_dev_t *i2s_dev, uint16_t data_width, uint32_t clk_freq)
|
||||
{
|
||||
// Configure the clock
|
||||
i2s_dev->clkm_conf.val = 0;
|
||||
i2s_dev->clkm_conf.clkm_div_num = 2; // 160MHz / 2 = 80MHz
|
||||
i2s_dev->clkm_conf.clkm_div_b = 0;
|
||||
i2s_dev->clkm_conf.clkm_div_a = 63;
|
||||
i2s_dev->clkm_conf.clk_sel = 2;
|
||||
i2s_dev->clkm_conf.clk_en = 1;
|
||||
|
||||
// Configure sampling rate
|
||||
i2s_dev->sample_rate_conf.tx_bck_div_num = 40000000 / clk_freq; // Fws = Fbck / 2
|
||||
i2s_dev->sample_rate_conf.tx_bits_mod = data_width;
|
||||
|
||||
i2s_dev->timing.val = 0;
|
||||
|
||||
i2s_dev->int_ena.val = 0;
|
||||
i2s_dev->int_clr.val = ~0;
|
||||
|
||||
i2s_dev->conf2.val = 0;
|
||||
i2s_dev->conf2.lcd_en = 1;
|
||||
|
||||
// Configuration data format
|
||||
i2s_dev->conf.val = 0;
|
||||
i2s_dev->conf.tx_right_first = 1;
|
||||
i2s_dev->conf.tx_msb_right = 1;
|
||||
i2s_dev->conf.tx_dma_equal = 1;
|
||||
|
||||
i2s_dev->conf1.tx_pcm_bypass = 1;
|
||||
i2s_dev->conf1.tx_stop_en = 1;
|
||||
|
||||
i2s_dev->fifo_conf.val = 0;
|
||||
i2s_dev->fifo_conf.dscr_en = 1;
|
||||
i2s_dev->fifo_conf.tx_fifo_mod_force_en = 1;
|
||||
i2s_dev->fifo_conf.tx_data_num = 32;
|
||||
i2s_dev->fifo_conf.tx_fifo_mod = 2;
|
||||
i2s_dev->fifo_conf.tx_24msb_en = 0;
|
||||
|
||||
i2s_dev->conf_chan.tx_chan_mod = 0;//remove
|
||||
i2s_dev->int_ena.out_eof = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_set_pin(const i2s_lcd_config_t *config)
|
||||
{
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_num_wr], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_num_wr, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_num_wr, GPIO_FLOATING);
|
||||
gpio_matrix_out(config->pin_num_wr, I2S0O_WS_OUT_IDX, true, false);
|
||||
|
||||
for (int i = 0; i < config->data_width; i++) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_data_num[i]], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(config->pin_data_num[i], GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(config->pin_data_num[i], GPIO_FLOATING);
|
||||
// High bit aligned, OUT23 is always the highest bit
|
||||
gpio_matrix_out(config->pin_data_num[i], I2S0O_DATA_OUT0_IDX + (LCD_DATA_MAX_WIDTH - config->data_width) + i, false, false);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_dma_config(i2s_lcd_obj_t *i2s_lcd_obj, uint32_t max_dma_buffer_size)
|
||||
{
|
||||
int cnt = 0;
|
||||
if (LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE % 2 != 0) {
|
||||
ESP_LOGE(TAG, "ESP32 only supports 2-byte aligned data length");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (max_dma_buffer_size >= LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE * 2) {
|
||||
i2s_lcd_obj->dma_node_buffer_size = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE;
|
||||
for (cnt = 0; cnt < max_dma_buffer_size - 8; cnt++) { // Find a buffer size that can divide dma_size
|
||||
if ((max_dma_buffer_size - cnt) % (i2s_lcd_obj->dma_node_buffer_size * 2) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i2s_lcd_obj->dma_buffer_size = max_dma_buffer_size - cnt;
|
||||
} else {
|
||||
i2s_lcd_obj->dma_node_buffer_size = max_dma_buffer_size / 2;
|
||||
i2s_lcd_obj->dma_buffer_size = i2s_lcd_obj->dma_node_buffer_size * 2;
|
||||
}
|
||||
|
||||
i2s_lcd_obj->dma_half_buffer_size = i2s_lcd_obj->dma_buffer_size / 2;
|
||||
i2s_lcd_obj->dma_node_cnt = (i2s_lcd_obj->dma_buffer_size) / i2s_lcd_obj->dma_node_buffer_size; // Number of DMA nodes
|
||||
i2s_lcd_obj->dma_half_node_cnt = i2s_lcd_obj->dma_node_cnt / 2;
|
||||
|
||||
ESP_LOGI(TAG, "lcd_buffer_size: %d, lcd_dma_size: %d, lcd_dma_node_cnt: %d", i2s_lcd_obj->dma_buffer_size, i2s_lcd_obj->dma_node_buffer_size, i2s_lcd_obj->dma_node_cnt);
|
||||
|
||||
i2s_lcd_obj->dma = (lldesc_t *)heap_caps_malloc(i2s_lcd_obj->dma_node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
i2s_lcd_obj->dma_buffer = (uint8_t *)heap_caps_malloc(i2s_lcd_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_cam_deinit(i2s_lcd_driver_t *drv)
|
||||
{
|
||||
if (!drv->i2s_lcd_obj) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (drv->i2s_lcd_obj->event_queue) {
|
||||
vQueueDelete(drv->i2s_lcd_obj->event_queue);
|
||||
}
|
||||
if (drv->i2s_lcd_obj->dma) {
|
||||
heap_caps_free(drv->i2s_lcd_obj->dma);
|
||||
}
|
||||
if (drv->i2s_lcd_obj->dma_buffer) {
|
||||
heap_caps_free(drv->i2s_lcd_obj->dma_buffer);
|
||||
}
|
||||
|
||||
if (drv->i2s_lcd_obj->lcd_cam_intr_handle) {
|
||||
esp_intr_free(drv->i2s_lcd_obj->lcd_cam_intr_handle);
|
||||
}
|
||||
|
||||
heap_caps_free(drv->i2s_lcd_obj);
|
||||
drv->i2s_lcd_obj = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_cam_init(i2s_lcd_driver_t *drv, const i2s_lcd_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
i2s_lcd_obj_t *i2s_lcd_obj = (i2s_lcd_obj_t *)heap_caps_calloc(1, sizeof(i2s_lcd_obj_t), MALLOC_CAP_DMA);
|
||||
if (i2s_lcd_obj == NULL) {
|
||||
ESP_LOGE(TAG, "lcd_cam object malloc error");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
drv->i2s_lcd_obj = i2s_lcd_obj;
|
||||
|
||||
if (I2S_NUM_0 == config->i2s_port) {
|
||||
i2s_lcd_obj->i2s_dev = &I2S0;
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Designated I2S peripheral not found");
|
||||
}
|
||||
|
||||
ret |= i2s_lcd_reg_config(i2s_lcd_obj->i2s_dev, config->data_width, config->clk_freq);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd_cam config fail!");
|
||||
lcd_cam_deinit(drv);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret |= lcd_set_pin(config);
|
||||
ret |= lcd_dma_config(i2s_lcd_obj, config->buffer_size);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd config fail!");
|
||||
lcd_cam_deinit(drv);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
i2s_lcd_obj->event_queue = xQueueCreate(1, sizeof(int));
|
||||
i2s_lcd_obj->width = config->data_width;
|
||||
i2s_lcd_obj->swap_data = config->swap_data;
|
||||
|
||||
if (i2s_lcd_obj->event_queue == NULL) {
|
||||
ESP_LOGE(TAG, "lcd config fail!");
|
||||
lcd_cam_deinit(drv);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret |= esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, i2s_isr, i2s_lcd_obj, &i2s_lcd_obj->lcd_cam_intr_handle);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd_cam intr alloc fail!");
|
||||
lcd_cam_deinit(drv);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "lcd init ok");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**< Public functions */
|
||||
|
||||
i2s_lcd_handle_t i2s_lcd_driver_init(const i2s_lcd_config_t *config)
|
||||
{
|
||||
I2S_CHECK(NULL != config, "config pointer invalid", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_GPIO(config->pin_num_wr), "GPIO WR invalid", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_GPIO(config->pin_num_rs), "GPIO RS invalid", NULL);
|
||||
I2S_CHECK(config->data_width > 0 && config->data_width <= 16, "Bit width out of range", NULL);
|
||||
I2S_CHECK(0 == (config->data_width % 8), "Bit width must be a multiple of 8", NULL);
|
||||
uint64_t pin_mask = 0;
|
||||
for (size_t i = 0; i < config->data_width; i++) {
|
||||
uint64_t mask = 1ULL << config->pin_data_num[i];
|
||||
I2S_CHECK(!(pin_mask & mask), "Data bus GPIO has a duplicate", NULL);
|
||||
I2S_CHECK(GPIO_IS_VALID_GPIO(config->pin_data_num[i]), "Data bus gpio invalid", NULL);
|
||||
pin_mask |= mask;
|
||||
}
|
||||
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)heap_caps_malloc(sizeof(i2s_lcd_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "Error malloc handle of i2s lcd driver", NULL);
|
||||
|
||||
esp_err_t ret = lcd_cam_init(i2s_lcd_drv, config);
|
||||
if(ESP_OK != ret) {
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "i2s lcd driver initialize failed");
|
||||
heap_caps_free(i2s_lcd_drv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i2s_lcd_drv->mutex = xSemaphoreCreateMutex();
|
||||
if (i2s_lcd_drv->mutex == NULL) {
|
||||
ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, "lcd create mutex failed");
|
||||
lcd_cam_deinit(i2s_lcd_drv);
|
||||
heap_caps_free(i2s_lcd_drv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (config->pin_num_cs >= 0) {
|
||||
gpio_pad_select_gpio(config->pin_num_cs);
|
||||
gpio_set_direction(config->pin_num_cs, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(config->pin_num_cs, 0);
|
||||
}
|
||||
|
||||
gpio_pad_select_gpio(config->pin_num_rs);
|
||||
gpio_set_direction(config->pin_num_rs, GPIO_MODE_OUTPUT);
|
||||
i2s_lcd_drv->rs_io_num = config->pin_num_rs;
|
||||
return (i2s_lcd_handle_t)i2s_lcd_drv;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_driver_deinit(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
lcd_cam_deinit(i2s_lcd_drv);
|
||||
vSemaphoreDelete(i2s_lcd_drv->mutex);
|
||||
heap_caps_free(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write_data(i2s_lcd_handle_t handle, uint16_t data)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
i2s_write_data(i2s_lcd_drv->i2s_lcd_obj, (uint8_t *)&data, i2s_lcd_drv->i2s_lcd_obj->width == 16 ? 2 : 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write_cmd(i2s_lcd_handle_t handle, uint16_t cmd)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
gpio_set_level(i2s_lcd_drv->rs_io_num, LCD_CMD_LEV);
|
||||
i2s_write_data(i2s_lcd_drv->i2s_lcd_obj, (uint8_t *)&cmd, i2s_lcd_drv->i2s_lcd_obj->width == 16 ? 2 : 1);
|
||||
gpio_set_level(i2s_lcd_drv->rs_io_num, LCD_DATA_LEV);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_write(i2s_lcd_handle_t handle, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
i2s_write_data(i2s_lcd_drv->i2s_lcd_obj, (uint8_t*)data, length);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_acquire(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
BaseType_t ret = xSemaphoreTake(i2s_lcd_drv->mutex, portMAX_DELAY);
|
||||
I2S_CHECK(pdTRUE == ret, "Take semaphore failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_lcd_release(i2s_lcd_handle_t handle)
|
||||
{
|
||||
i2s_lcd_driver_t *i2s_lcd_drv = (i2s_lcd_driver_t *)handle;
|
||||
I2S_CHECK(NULL != i2s_lcd_drv, "handle pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
BaseType_t ret = xSemaphoreGive(i2s_lcd_drv->mutex);
|
||||
I2S_CHECK(pdTRUE == ret, "Give semaphore failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
@@ -0,0 +1,296 @@
|
||||
// Copyright 2019-2020 Espressif Systems (Shanghai) PTE 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.
|
||||
#ifndef _I2C_BUS_H_
|
||||
#define _I2C_BUS_H_
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define NULL_I2C_MEM_ADDR 0xFF /*!< set mem_address to NULL_I2C_MEM_ADDR if i2c device has no internal address during read/write */
|
||||
#define NULL_I2C_DEV_ADDR 0xFF /*!< invalid i2c device address */
|
||||
typedef void *i2c_bus_handle_t; /*!< i2c bus handle */
|
||||
typedef void *i2c_bus_device_handle_t; /*!< i2c device handle */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**************************************** Public Functions (Application level)*********************************************/
|
||||
|
||||
/**
|
||||
* @brief Create an I2C bus instance then return a handle if created successfully. Each I2C bus works in a singleton mode,
|
||||
* which means for an i2c port only one group parameter works. When i2c_bus_create is called more than one time for the
|
||||
* same i2c port, following parameter will override the previous one.
|
||||
*
|
||||
* @param port I2C port number
|
||||
* @param conf Pointer to I2C bus configuration
|
||||
* @return i2c_bus_handle_t Return the I2C bus handle if created successfully, return NULL if failed.
|
||||
*/
|
||||
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf);
|
||||
|
||||
/**
|
||||
* @brief Delete and release the I2C bus resource.
|
||||
*
|
||||
* @param p_bus_handle Point to the I2C bus handle, if delete succeed handle will set to NULL.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Scan i2c devices attached on i2c bus
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @param buf Pointer to a buffer to save devices' address, if NULL no address will be saved.
|
||||
* @param num Maximum number of addresses to save, invalid if buf set to NULL,
|
||||
* higer addresses will be discarded if num less-than the total number found on the I2C bus.
|
||||
* @return uint8_t Total number of devices found on the I2C bus
|
||||
*/
|
||||
uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num);
|
||||
|
||||
/**
|
||||
* @brief Get current active clock speed.
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @return uint32_t current clock speed
|
||||
*/
|
||||
uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Get created device number of the bus.
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @return uint8_t created device number of the bus
|
||||
*/
|
||||
uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Create an I2C device on specific bus.
|
||||
* Dynamic configuration must be enable to achieve multiple devices with different configs on a single bus.
|
||||
* menuconfig:Bus Options->I2C Bus Options->enable dynamic configuration
|
||||
*
|
||||
* @param bus_handle Point to the I2C bus handle
|
||||
* @param dev_addr i2c device address
|
||||
* @param clk_speed device specified clock frequency the i2c_bus will switch to during each transfer. 0 if use current bus speed.
|
||||
* @return i2c_bus_device_handle_t return a device handle if created successfully, return NULL if failed.
|
||||
*/
|
||||
i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed);
|
||||
|
||||
/**
|
||||
* @brief Delete and release the I2C device resource, i2c_bus_device_delete should be used in pairs with i2c_bus_device_create.
|
||||
*
|
||||
* @param p_dev_handle Point to the I2C device handle, if delete succeed handle will set to NULL.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Get device's I2C address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @return uint8_t I2C address, return NULL_I2C_DEV_ADDR if dev_handle is invalid.
|
||||
*/
|
||||
uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Read single byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple bytes from i2c device with 8-bit internal register/memory address.
|
||||
* If internal reg/mem address is 16-bit, please refer i2c_bus_read_reg16
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read single bit of a byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_num The bit number 0 - 7 to read
|
||||
* @param data Pointer to a buffer to save the data that was read. *data == 0 -> bit = 0, *data !=0 -> bit = 1.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple bits of a byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_start The bit to start from, 0 - 7, MSB at 0
|
||||
* @param length The number of bits to read, 1 - 8
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write single byte to i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data The byte to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Write multiple byte to i2c device with 8-bit internal register/memory address
|
||||
* If internal reg/mem address is 16-bit, please refer i2c_bus_write_reg16
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write single bit of a byte to an i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_num The bit number 0 - 7 to write
|
||||
* @param data The bit to write, data == 0 means set bit = 0, data !=0 means set bit = 1.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Write multiple bits of a byte to an i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_start The bit to start from, 0 - 7, MSB at 0
|
||||
* @param length The number of bits to write, 1 - 8
|
||||
* @param data The bits to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t data);
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
/**
|
||||
* @brief I2C master send queued commands create by ``i2c_cmd_link_create`` .
|
||||
* This function will trigger sending all queued commands.
|
||||
* The task will be blocked until all the commands have been sent out.
|
||||
* If I2C_BUS_DYNAMIC_CONFIG enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
* hence multiple devices with different configs on a single bus can be supported.
|
||||
* @note
|
||||
* Only call this function when ``i2c_bus_read/write_xx`` do not meet the requirements
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param cmd I2C command handler
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_cmd_begin(i2c_bus_device_handle_t dev_handle, i2c_cmd_handle_t cmd);
|
||||
|
||||
/**
|
||||
* @brief Write date to an i2c device with 16-bit internal reg/mem address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal 16-bit reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read date from i2c device with 16-bit internal reg/mem address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal 16-bit reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, uint8_t *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE 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.
|
||||
#ifndef __I2S_LCD_DRIVER_H__
|
||||
#define __I2S_LCD_DRIVER_H__
|
||||
|
||||
#include "driver/i2s.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define LCD_CMD_LEV (0)
|
||||
#define LCD_DATA_LEV (1)
|
||||
|
||||
typedef void * i2s_lcd_handle_t; /** Handle of i2s lcd driver */
|
||||
|
||||
/**
|
||||
* @brief Configuration of i2s lcd mode
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int8_t data_width; /*!< Parallel data width, 16bit or 8bit available */
|
||||
int8_t pin_data_num[16]; /*!< Parallel data output IO*/
|
||||
int8_t pin_num_cs; /*!< CS io num */
|
||||
int8_t pin_num_wr; /*!< Write clk io*/
|
||||
int8_t pin_num_rs; /*!< RS io num */
|
||||
int clk_freq; /*!< I2s clock frequency */
|
||||
i2s_port_t i2s_port; /*!< I2S port number */
|
||||
bool swap_data; /*!< Swap the 2 bytes of RGB565 color */
|
||||
uint32_t buffer_size; /*!< DMA buffer size */
|
||||
} i2s_lcd_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initilize i2s lcd driver.
|
||||
*
|
||||
* @param config configuration of i2s
|
||||
*
|
||||
* @return A handle to the created i2s lcd driver, or NULL in case of error.
|
||||
*/
|
||||
i2s_lcd_handle_t i2s_lcd_driver_init(const i2s_lcd_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinit i2s lcd driver.
|
||||
*
|
||||
* @param handle i2s lcd driver handle to deinitilize
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG handle is invalid
|
||||
*/
|
||||
esp_err_t i2s_lcd_driver_deinit(i2s_lcd_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Write a data to LCD
|
||||
*
|
||||
* @param handle i2s lcd driver handle
|
||||
* @param data Data to write
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG handle is invalid
|
||||
*/
|
||||
esp_err_t i2s_lcd_write_data(i2s_lcd_handle_t handle, uint16_t data);
|
||||
|
||||
/**
|
||||
* @brief Write a command to LCD
|
||||
*
|
||||
* @param handle Handle of i2s lcd driver
|
||||
* @param cmd command to write
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG handle is invalid
|
||||
*/
|
||||
esp_err_t i2s_lcd_write_cmd(i2s_lcd_handle_t handle, uint16_t cmd);
|
||||
|
||||
/**
|
||||
* @brief Write block data to LCD
|
||||
*
|
||||
* @param handle Handle of i2s lcd driver
|
||||
* @param data Pointer of data
|
||||
* @param length length of data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG handle is invalid
|
||||
*/
|
||||
esp_err_t i2s_lcd_write(i2s_lcd_handle_t handle, const uint8_t *data, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief acquire a lock
|
||||
*
|
||||
* @param handle Handle of i2s lcd driver
|
||||
*
|
||||
* @return Always return ESP_OK
|
||||
*/
|
||||
esp_err_t i2s_lcd_acquire(i2s_lcd_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief release a lock
|
||||
*
|
||||
* @param handle Handle of i2s lcd driver
|
||||
*
|
||||
* @return Always return ESP_OK
|
||||
*/
|
||||
esp_err_t i2s_lcd_release(i2s_lcd_handle_t handle);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE 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.
|
||||
#ifndef _IOT_SPI_BUS_H_
|
||||
#define _IOT_SPI_BUS_H_
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#define NULL_SPI_CS_PIN -1 /*!< set cs_io_num to NULL_SPI_CS_PIN if spi device has no CP pin */
|
||||
typedef void *spi_bus_handle_t; /*!< spi bus handle */
|
||||
typedef void *spi_bus_device_handle_t; /*!< spi device handle */
|
||||
|
||||
/**
|
||||
* spi bus initialization parameters.
|
||||
* */
|
||||
typedef struct {
|
||||
gpio_num_t miso_io_num; /*!< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.*/
|
||||
gpio_num_t mosi_io_num; /*!< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.*/
|
||||
gpio_num_t sclk_io_num; /*!< GPIO pin for Spi CLocK signal, or -1 if not used*/
|
||||
int max_transfer_sz; /*!< <Maximum length of bytes available to send, if < 4096, 4096 will be set*/
|
||||
}spi_config_t;
|
||||
|
||||
/**
|
||||
* spi device initialization parameters.
|
||||
* */
|
||||
typedef struct {
|
||||
gpio_num_t cs_io_num; /*!< GPIO pin to select this device (CS), or -1 if not used*/
|
||||
uint8_t mode; /*!< modes (0,1,2,3) that correspond to the four possible clocking configurations*/
|
||||
int clock_speed_hz; /*!< spi clock speed, divisors of 80MHz, in Hz. See ``SPI_MASTER_FREQ_*`*/
|
||||
}spi_device_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create and initialize a spi bus and return the spi bus handle
|
||||
*
|
||||
* @param host_id SPI peripheral that controls this bus, SPI2_HOST or SPI3_HOST
|
||||
* @param bus_conf spi bus configurations details in spi_config_t
|
||||
* @return spi_bus_handle_t handle for spi bus operation, NULL if failed.
|
||||
*/
|
||||
spi_bus_handle_t spi_bus_create(spi_host_device_t host_id, const spi_config_t *bus_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize and delete the spi bus
|
||||
*
|
||||
* @param p_bus_handle pointer to spi bus handle, if delete succeed handle will set to NULL.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_FAIL Fail
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t spi_bus_delete(spi_bus_handle_t *p_bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Create and add a device on the spi bus.
|
||||
*
|
||||
* @param bus_handle handle for spi bus operation.
|
||||
* @param device_conf spi device configurations details in spi_device_config_t
|
||||
* @return spi_bus_device_handle_t handle for device operation, NULL if failed.
|
||||
*/
|
||||
spi_bus_device_handle_t spi_bus_device_create(spi_bus_handle_t bus_handle, const spi_device_config_t *device_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize and remove the device from spi bus.
|
||||
*
|
||||
* @param p_dev_handle pointer to device handle, if delete succeed handle will set to NULL.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_FAIL Fail
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t spi_bus_device_delete(spi_bus_device_handle_t *p_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Transfer one byte with the device.
|
||||
*
|
||||
* @param dev_handle handle for device operation.
|
||||
* @param data_out data will send to device.
|
||||
* @param data_in pointer to receive buffer, set NULL to skip receive phase.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_TIMEOUT if bus is busy
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_bus_transfer_byte(spi_bus_device_handle_t dev_handle, uint8_t data_out, uint8_t *data_in);
|
||||
|
||||
/**
|
||||
* @brief Transfer multi-bytes with the device.
|
||||
*
|
||||
* @param dev_handle handle for device operation.
|
||||
* @param data_out pointer to sent buffer, set NULL to skip sent phase.
|
||||
* @param data_in pointer to receive buffer, set NULL to skip receive phase.
|
||||
* @param data_len number of bytes will transfer.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_TIMEOUT if bus is busy
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_bus_transfer_bytes(spi_bus_device_handle_t dev_handle, const uint8_t *data_out, uint8_t *data_in, uint32_t data_len);
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
/**
|
||||
* @brief Send a polling transaction, wait for it to complete, and return the result
|
||||
* @note
|
||||
* Only call this function when ``spi_bus_transfer_xx`` do not meet the requirements
|
||||
*
|
||||
* @param dev_handle handle for device operation.
|
||||
* @param p_trans Description of transaction to execute
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_TIMEOUT if bus is busy
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_bus_transmit_begin(spi_bus_device_handle_t dev_handle, spi_transaction_t *p_trans);
|
||||
|
||||
/**
|
||||
* @brief Transfer one 16-bit value with the device. using msb by default.
|
||||
* For example 0x1234, 0x12 will send first then 0x34.
|
||||
*
|
||||
* @param dev_handle handle for device operation.
|
||||
* @param data_out data will send to device.
|
||||
* @param data_in pointer to receive buffer, set NULL to skip receive phase.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_TIMEOUT if bus is busy
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_bus_transfer_reg16(spi_bus_device_handle_t dev_handle, uint16_t data_out, uint16_t *data_in);
|
||||
|
||||
/**
|
||||
* @brief Transfer one 32-bit value with the device. using msb by default.
|
||||
* For example 0x12345678, 0x12 will send first, 0x78 will send in the end.
|
||||
*
|
||||
* @param dev_handle handle for device operation.
|
||||
* @param data_out data will send to device.
|
||||
* @param data_in pointer to receive buffer, set NULL to skip receive phase.
|
||||
* @return esp_err_t
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_TIMEOUT if bus is busy
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_bus_transfer_reg32(spi_bus_device_handle_t dev_handle, uint32_t data_out, uint32_t *data_in);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_common.h"
|
||||
#include "bus/include/spi_bus.h"
|
||||
|
||||
typedef struct {
|
||||
spi_host_device_t host_id; /*!<spi device number */
|
||||
bool is_init;
|
||||
spi_bus_config_t conf; /*!<spi bus active configuration */
|
||||
} _spi_bus_t;
|
||||
|
||||
typedef struct {
|
||||
spi_device_handle_t handle;
|
||||
spi_bus_handle_t spi_bus; /*!<spi bus handle */
|
||||
spi_device_interface_config_t conf; /*!<spi device active configuration */
|
||||
SemaphoreHandle_t mutex; /* mutex to achive device thread-safe*/
|
||||
} _spi_device_t;
|
||||
|
||||
static const char *TAG = "spi_bus";
|
||||
static _spi_bus_t s_spi_bus[2];
|
||||
#define ESP_SPI_MUTEX_TICKS_TO_WAIT 2
|
||||
|
||||
#define SPI_BUS_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define SPI_BUS_CHECK_GOTO(a, str, lable) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
goto lable; \
|
||||
}
|
||||
|
||||
#define SPI_DEVICE_MUTEX_TAKE(p_spi_dev, ret) if (!xSemaphoreTake((p_spi_dev)->mutex, ESP_SPI_MUTEX_TICKS_TO_WAIT)) { \
|
||||
ESP_LOGE(TAG, "spi device(%d) take mutex timeout, max wait = %d ticks", (int32_t)((p_spi_dev)->handle), ESP_SPI_MUTEX_TICKS_TO_WAIT); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define SPI_DEVICE_MUTEX_GIVE(p_spi_dev, ret) if (!xSemaphoreGive((p_spi_dev)->mutex)) { \
|
||||
ESP_LOGE(TAG, "spi device(%d) give mutex failed", (int32_t)((p_spi_dev)->handle)); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
spi_bus_handle_t spi_bus_create(spi_host_device_t host_id, const spi_config_t *bus_conf)
|
||||
{
|
||||
SPI_BUS_CHECK(SPI1_HOST < host_id && host_id <= SPI3_HOST, "Invalid spi host_id", NULL);
|
||||
uint8_t index = host_id - 1; //find related index
|
||||
spi_bus_config_t buscfg = {
|
||||
.miso_io_num = bus_conf->miso_io_num,
|
||||
.mosi_io_num = bus_conf->mosi_io_num,
|
||||
.sclk_io_num = bus_conf->sclk_io_num,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = bus_conf->max_transfer_sz,
|
||||
};
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
|
||||
esp_err_t ret = spi_bus_initialize(host_id, &buscfg, SPI_DMA_CH_AUTO);
|
||||
#else
|
||||
int dma_chan = host_id; //set dma channel equals to host_id by default
|
||||
esp_err_t ret = spi_bus_initialize(host_id, &buscfg, dma_chan);
|
||||
#endif
|
||||
SPI_BUS_CHECK(ESP_OK == ret, "spi bus create failed", NULL);
|
||||
s_spi_bus[index].host_id = host_id;
|
||||
memcpy(&s_spi_bus[index].conf, &buscfg, sizeof(spi_bus_config_t));
|
||||
s_spi_bus[index].is_init = true;
|
||||
ESP_LOGI(TAG, "SPI%d bus created", host_id + 1);
|
||||
return (spi_bus_handle_t)&s_spi_bus[index];
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_delete(spi_bus_handle_t *p_bus_handle)
|
||||
{
|
||||
SPI_BUS_CHECK((NULL != p_bus_handle) && (NULL != *p_bus_handle), "Handle error", ESP_ERR_INVALID_ARG);
|
||||
_spi_bus_t *spi_bus = (_spi_bus_t *)(*p_bus_handle);
|
||||
|
||||
if (!spi_bus->is_init) {
|
||||
ESP_LOGW(TAG, "spi_bus%d has been de-inited", spi_bus->host_id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t ret = spi_bus_free(spi_bus->host_id);
|
||||
SPI_BUS_CHECK(ESP_OK == ret, "spi bus delete failed", ESP_FAIL);
|
||||
ESP_LOGI(TAG, "SPI%d bus delete", spi_bus->host_id + 1);
|
||||
memset(spi_bus, 0, sizeof(_spi_bus_t));
|
||||
*p_bus_handle = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
spi_bus_device_handle_t spi_bus_device_create(spi_bus_device_handle_t bus_handle, const spi_device_config_t *device_conf)
|
||||
{
|
||||
SPI_BUS_CHECK(NULL != bus_handle, "Pointer error", NULL);
|
||||
_spi_bus_t *spi_bus = (_spi_bus_t *)bus_handle;
|
||||
|
||||
_spi_device_t *spi_dev = malloc(sizeof(_spi_device_t));
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.clock_speed_hz = device_conf->clock_speed_hz,
|
||||
.duty_cycle_pos = 128, //50% duty cycle
|
||||
.mode = device_conf->mode,
|
||||
.spics_io_num = device_conf->cs_io_num,
|
||||
.cs_ena_posttrans = 3, //Keep the CS low 3 cycles after transaction, to stop slave from missing the last bit when CS has less propagation delay than CLK
|
||||
.queue_size = 3
|
||||
};
|
||||
esp_err_t ret = spi_bus_add_device(spi_bus->host_id, &devcfg, &spi_dev->handle);
|
||||
SPI_BUS_CHECK_GOTO(ESP_OK == ret, "add spi device failed", cleanup_device);
|
||||
spi_dev->mutex = xSemaphoreCreateMutex();
|
||||
SPI_BUS_CHECK_GOTO(NULL != spi_dev->mutex, "spi device create mutex failed", cleanup_device);
|
||||
spi_dev->spi_bus = bus_handle;
|
||||
memcpy(&spi_dev->conf, &devcfg, sizeof(spi_device_interface_config_t));
|
||||
ESP_LOGI(TAG, "SPI%d bus device added, CS=%d Mode=%u Speed=%d", spi_bus->host_id + 1, device_conf->cs_io_num, device_conf->mode, device_conf->clock_speed_hz);
|
||||
return (spi_bus_device_handle_t)spi_dev;
|
||||
|
||||
cleanup_device:
|
||||
free(spi_dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_device_delete(spi_bus_device_handle_t *p_dev_handle)
|
||||
{
|
||||
SPI_BUS_CHECK((NULL != p_dev_handle) && (NULL != *p_dev_handle), "Pointer error", ESP_ERR_INVALID_ARG);
|
||||
_spi_device_t *spi_dev = (_spi_device_t *)(*p_dev_handle);
|
||||
_spi_bus_t *spi_bus = (_spi_bus_t *)(spi_dev->spi_bus);
|
||||
SPI_DEVICE_MUTEX_TAKE(spi_dev, ESP_FAIL);
|
||||
esp_err_t ret = spi_bus_remove_device(spi_dev->handle);
|
||||
SPI_DEVICE_MUTEX_GIVE(spi_dev, ESP_FAIL);
|
||||
SPI_BUS_CHECK(ESP_OK == ret, "spi bus delete device failed", ret);
|
||||
vSemaphoreDelete(spi_dev->mutex);
|
||||
ESP_LOGI(TAG, "SPI%d device removed, CS=%d", spi_bus->host_id + 1, spi_dev->conf.spics_io_num);
|
||||
free(spi_dev);
|
||||
*p_dev_handle = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* this function should lable with inline*/
|
||||
inline static esp_err_t _spi_device_polling_transmit(spi_bus_device_handle_t dev_handle, spi_transaction_t *trans)
|
||||
{
|
||||
SPI_BUS_CHECK(NULL != dev_handle, "Pointer error", ESP_ERR_INVALID_ARG);
|
||||
_spi_device_t *spi_dev = (_spi_device_t *)(dev_handle);
|
||||
esp_err_t ret;
|
||||
SPI_DEVICE_MUTEX_TAKE(spi_dev, ESP_FAIL);
|
||||
ret = spi_device_polling_transmit(spi_dev->handle, trans);
|
||||
SPI_DEVICE_MUTEX_GIVE(spi_dev, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_transfer_byte(spi_bus_device_handle_t dev_handle, uint8_t data_out, uint8_t *data_in)
|
||||
{
|
||||
esp_err_t ret;
|
||||
spi_transaction_t trans = {
|
||||
.length = 8,
|
||||
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,
|
||||
.tx_data = {
|
||||
[0] = data_out
|
||||
}
|
||||
};
|
||||
ret = _spi_device_polling_transmit(dev_handle, &trans);
|
||||
SPI_BUS_CHECK(ret == ESP_OK, "spi transfer byte failed", ret);
|
||||
|
||||
if (data_in) {
|
||||
*data_in = trans.rx_data[0];
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_transfer_bytes(spi_bus_device_handle_t dev_handle, const uint8_t *data_out, uint8_t *data_in, uint32_t data_len)
|
||||
{
|
||||
esp_err_t ret;
|
||||
#if 1
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
uint32_t remain = data_len;
|
||||
while (remain > 0) {
|
||||
uint32_t chunk_len = MIN(remain, 2048);
|
||||
spi_transaction_t trans = {
|
||||
.length = chunk_len * 8,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = NULL
|
||||
};
|
||||
|
||||
if (data_out) {
|
||||
trans.tx_buffer = data_out+(data_len-remain);
|
||||
}
|
||||
|
||||
if (data_in) {
|
||||
trans.rx_buffer = data_in+(data_len-remain);
|
||||
}
|
||||
ret = _spi_device_polling_transmit(dev_handle, &trans);
|
||||
SPI_BUS_CHECK(ret == ESP_OK, "spi transfer bytes failed", ret);
|
||||
remain -= chunk_len;
|
||||
}
|
||||
#else
|
||||
spi_transaction_t trans = {
|
||||
.length = data_len * 8,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = NULL
|
||||
};
|
||||
|
||||
if (data_out) {
|
||||
trans.tx_buffer = data_out;
|
||||
}
|
||||
|
||||
if (data_in) {
|
||||
trans.rx_buffer = data_in;
|
||||
}
|
||||
|
||||
ret = _spi_device_polling_transmit(dev_handle, &trans);
|
||||
SPI_BUS_CHECK(ret == ESP_OK, "spi transfer bytes failed", ret);
|
||||
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
esp_err_t spi_bus_transmit_begin(spi_bus_device_handle_t dev_handle, spi_transaction_t *p_trans)
|
||||
{
|
||||
return _spi_device_polling_transmit(dev_handle, p_trans);
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_transfer_reg16(spi_bus_device_handle_t dev_handle, uint16_t data_out, uint16_t *data_in)
|
||||
{
|
||||
esp_err_t ret;
|
||||
spi_transaction_t trans = {
|
||||
.length = 16,
|
||||
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,
|
||||
/* default MSB first */
|
||||
.tx_data = {
|
||||
[0] = (data_out >> 8) & 0xff,
|
||||
[1] = data_out & 0xff,
|
||||
}
|
||||
};
|
||||
ret = _spi_device_polling_transmit(dev_handle, &trans);
|
||||
SPI_BUS_CHECK(ret == ESP_OK, "spi transfer reg16 failed", ret);
|
||||
|
||||
if (data_in) {
|
||||
*data_in = (trans.rx_data[0] << 8) | (trans.rx_data[1]);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t spi_bus_transfer_reg32(spi_bus_device_handle_t dev_handle, uint32_t data_out, uint32_t *data_in)
|
||||
{
|
||||
esp_err_t ret;
|
||||
spi_transaction_t trans = {
|
||||
.length = 32,
|
||||
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,
|
||||
/* default MSB first */
|
||||
.tx_data = {
|
||||
[0] = (data_out >> 24) & 0xff,
|
||||
[1] = (data_out >> 16) & 0xff,
|
||||
[2] = (data_out >> 8) & 0xff,
|
||||
[3] = data_out & 0xff
|
||||
}
|
||||
};
|
||||
ret = _spi_device_polling_transmit(dev_handle, &trans);
|
||||
SPI_BUS_CHECK(ret == ESP_OK, "spi transfer reg32 failed", ret);
|
||||
|
||||
if (data_in) {
|
||||
*data_in = (trans.rx_data[0] << 24) | (trans.rx_data[1] << 16) | (trans.rx_data[2] << 8) | (trans.rx_data[3]);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
set(SCREEN_DIR "controller_driver/ili9341"
|
||||
"controller_driver/st7789"
|
||||
"controller_driver/st7796"
|
||||
"controller_driver/nt35510"
|
||||
"controller_driver/ili9806"
|
||||
"controller_driver/ili9486"
|
||||
"controller_driver/ili9488"
|
||||
"controller_driver/ssd1351"
|
||||
"controller_driver/rm68120"
|
||||
"controller_driver/ssd1306"
|
||||
"controller_driver/ssd1307"
|
||||
"controller_driver/ssd1322"
|
||||
"controller_driver/ssd1963"
|
||||
)
|
||||
|
||||
idf_component_register(SRC_DIRS "${SCREEN_DIR}" "screen_utility" "interface_driver" "."
|
||||
INCLUDE_DIRS "${SCREEN_DIR}" "interface_driver" "."
|
||||
PRIV_INCLUDE_DIRS "screen_utility"
|
||||
REQUIRES bus
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
menu "LCD Drivers"
|
||||
|
||||
menu "Select Screen Controller"
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ILI9341
|
||||
bool "ILI9341"
|
||||
default y
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ILI9486
|
||||
bool "ILI9486"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ILI9806
|
||||
bool "ILI9806"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ILI9488
|
||||
bool "ILI9488"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_NT35510
|
||||
bool "NT35510"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_RM68120
|
||||
bool "RM68120"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_SSD1351
|
||||
bool "SSD1351"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_SSD1963
|
||||
bool "SSD1963"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ST7789
|
||||
bool "ST7789"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_ST7796
|
||||
bool "ST7796"
|
||||
default n
|
||||
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_SSD1306
|
||||
bool "SSD1306"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_SSD1307
|
||||
bool "SSD1307"
|
||||
default n
|
||||
config LCD_DRIVER_SCREEN_CONTROLLER_SSD1322
|
||||
bool "SSD1322"
|
||||
default n
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
SCREEN_DIR = controller_driver/ili9341 \
|
||||
controller_driver/st7789 \
|
||||
controller_driver/st7796 \
|
||||
controller_driver/nt35510 \
|
||||
controller_driver/ili9806 \
|
||||
controller_driver/ili9486 \
|
||||
controller_driver/ili9488 \
|
||||
controller_driver/ssd1351 \
|
||||
controller_driver/rm68120 \
|
||||
controller_driver/ssd1306 \
|
||||
controller_driver/ssd1307 \
|
||||
controller_driver/ssd1322 \
|
||||
controller_driver/ssd1963
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := . iface_driver $(SCREEN_DIR) screen_utility
|
||||
COMPONENT_SRCDIRS := . iface_driver $(SCREEN_DIR) screen_utility
|
||||
@@ -0,0 +1,390 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ili9341.h"
|
||||
|
||||
static const char *TAG = "lcd ili9341";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "ILI9341"
|
||||
#define LCD_BPP 16
|
||||
|
||||
/** commands of ILI9341 */
|
||||
#define LCD_SWRESET 0x01 // Software Reset
|
||||
#define LCD_RDDID 0x04 // Read Display ID
|
||||
#define LCD_INVOFF 0x20 // Display Inversion Off
|
||||
#define LCD_INVON 0x21 // Display Inversion On
|
||||
#define LCD_CASET 0x2A // Column Address Set
|
||||
#define LCD_PASET 0x2B // Row Address Set
|
||||
#define LCD_RAMWR 0x2C // Memory Writ
|
||||
#define LCD_RAMRD 0x2E // Memory Read
|
||||
#define LCD_MADCTL 0x36 // Memory Data Access Contro
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define ILI9341_RESOLUTION_HOR 240
|
||||
#define ILI9341_RESOLUTION_VER 320
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ili9341_default_driver = {
|
||||
.init = lcd_ili9341_init,
|
||||
.deinit = lcd_ili9341_deinit,
|
||||
.set_direction = lcd_ili9341_set_rotation,
|
||||
.set_window = lcd_ili9341_set_window,
|
||||
.write_ram_data = lcd_ili9341_write_ram_data,
|
||||
.draw_pixel = lcd_ili9341_draw_pixel,
|
||||
.draw_bitmap = lcd_ili9341_draw_bitmap,
|
||||
.get_info = lcd_ili9341_get_info,
|
||||
};
|
||||
|
||||
static esp_err_t lcd_ili9341_init_reg(void);
|
||||
|
||||
esp_err_t lcd_ili9341_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ILI9341_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ILI9341_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
ret = lcd_ili9341_init_reg();
|
||||
LCD_CHECK(ESP_OK == ret, "Write lcd register encounter error", ESP_FAIL);
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
return lcd_ili9341_set_rotation(lcd_conf->rotate);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data |= MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=%x", reg_data);
|
||||
ret = LCD_WRITE_REG(LCD_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ILI9341_RESOLUTION_HOR, ILI9341_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(LCD_PASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_set_invert(bool is_invert)
|
||||
{
|
||||
return LCD_WRITE_CMD(is_invert ? LCD_INVON : LCD_INVOFF);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ili9341_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ili9341_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9341_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ili9341_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_ili9341_init_reg(void)
|
||||
{
|
||||
//SOFTWARE RESET
|
||||
LCD_WRITE_CMD(0x01);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
//POWER CONTROL A
|
||||
LCD_WRITE_CMD(0xCB);
|
||||
LCD_WRITE_DATA(0x39);
|
||||
LCD_WRITE_DATA(0x2C);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x34);
|
||||
LCD_WRITE_DATA(0x02);
|
||||
|
||||
//POWER CONTROL B
|
||||
LCD_WRITE_CMD(0xCF);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0xC1);
|
||||
LCD_WRITE_DATA(0x30);
|
||||
|
||||
//DRIVER TIMING CONTROL A
|
||||
LCD_WRITE_CMD(0xE8);
|
||||
LCD_WRITE_DATA(0x85);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x78);
|
||||
|
||||
//DRIVER TIMING CONTROL B
|
||||
LCD_WRITE_CMD(0xEA);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
//POWER ON SEQUENCE CONTROL
|
||||
LCD_WRITE_CMD(0xED);
|
||||
LCD_WRITE_DATA(0x64);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x12);
|
||||
LCD_WRITE_DATA(0x81);
|
||||
|
||||
//PUMP RATIO CONTROL
|
||||
LCD_WRITE_CMD(0xF7);
|
||||
LCD_WRITE_DATA(0x20);
|
||||
|
||||
//POWER CONTROL,VRH[5:0]
|
||||
LCD_WRITE_CMD(0xC0);
|
||||
LCD_WRITE_DATA(0x23);
|
||||
|
||||
//POWER CONTROL,SAP[2:0];BT[3:0]
|
||||
LCD_WRITE_CMD(0xC1);
|
||||
LCD_WRITE_DATA(0x10);
|
||||
|
||||
//VCM CONTROL
|
||||
LCD_WRITE_CMD(0xC5);
|
||||
LCD_WRITE_DATA(0x3E);
|
||||
LCD_WRITE_DATA(0x28);
|
||||
|
||||
//VCM CONTROL 2
|
||||
LCD_WRITE_CMD(0xC7);
|
||||
LCD_WRITE_DATA(0x86);
|
||||
|
||||
//MEMORY ACCESS CONTROL
|
||||
LCD_WRITE_CMD(0x36);
|
||||
LCD_WRITE_DATA(0x48);
|
||||
|
||||
//PIXEL FORMAT
|
||||
LCD_WRITE_CMD(0x3A);
|
||||
LCD_WRITE_DATA(0x55);
|
||||
|
||||
//FRAME RATIO CONTROL, STANDARD RGB COLOR
|
||||
LCD_WRITE_CMD(0xB1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x18);
|
||||
|
||||
//DISPLAY FUNCTION CONTROL
|
||||
LCD_WRITE_CMD(0xB6);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x82);
|
||||
LCD_WRITE_DATA(0x27);
|
||||
|
||||
//3GAMMA FUNCTION DISABLE
|
||||
LCD_WRITE_CMD(0xF2);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
//GAMMA CURVE SELECTED
|
||||
LCD_WRITE_CMD(0x26);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
|
||||
//POSITIVE GAMMA CORRECTION
|
||||
LCD_WRITE_CMD(0xE0);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x31);
|
||||
LCD_WRITE_DATA(0x2B);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x0E);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x4E);
|
||||
LCD_WRITE_DATA(0xF1);
|
||||
LCD_WRITE_DATA(0x37);
|
||||
LCD_WRITE_DATA(0x07);
|
||||
LCD_WRITE_DATA(0x10);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x0E);
|
||||
LCD_WRITE_DATA(0x09);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
//NEGATIVE GAMMA CORRECTION
|
||||
LCD_WRITE_CMD(0xE1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x0E);
|
||||
LCD_WRITE_DATA(0x14);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x11);
|
||||
LCD_WRITE_DATA(0x07);
|
||||
LCD_WRITE_DATA(0x31);
|
||||
LCD_WRITE_DATA(0xC1);
|
||||
LCD_WRITE_DATA(0x48);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x31);
|
||||
LCD_WRITE_DATA(0x36);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
|
||||
//EXIT SLEEP
|
||||
LCD_WRITE_CMD(0x11);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
//TURN ON DISPLAY
|
||||
LCD_WRITE_CMD(0x29);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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.
|
||||
#ifndef _IOT_LCD_ILI9341_H_
|
||||
#define _IOT_LCD_ILI9341_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_ili9341_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_set_invert(bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9341_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,344 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ili9486.h"
|
||||
|
||||
static const char *TAG = "ILI9486";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define ILI9486_CASET 0x2A
|
||||
#define ILI9486_RASET 0x2B
|
||||
#define ILI9486_RAMWR 0x2C
|
||||
#define ILI9486_MADCTL 0x36
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define LCD_NAME "ILI9486"
|
||||
#define LCD_BPP 16
|
||||
|
||||
#define ILI9486_RESOLUTION_HOR 320
|
||||
#define ILI9486_RESOLUTION_VER 480
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ili9486_default_driver = {
|
||||
.init = lcd_ili9486_init,
|
||||
.deinit = lcd_ili9486_deinit,
|
||||
.set_direction = lcd_ili9486_set_rotation,
|
||||
.set_window = lcd_ili9486_set_window,
|
||||
.write_ram_data = lcd_ili9486_write_ram_data,
|
||||
.draw_pixel = lcd_ili9486_draw_pixel,
|
||||
.draw_bitmap = lcd_ili9486_draw_bitmap,
|
||||
.get_info = lcd_ili9486_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void lcd_ili9486_init_reg(void);
|
||||
|
||||
esp_err_t lcd_ili9486_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ILI9486_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ILI9486_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_ili9486_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_ili9486_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(ILI9486_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ILI9486_RESOLUTION_HOR, ILI9486_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9486_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(ILI9486_RASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9486_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ili9486_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ili9486_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9486_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ili9486_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static void lcd_ili9486_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x01); // SW reset
|
||||
vTaskDelay(120 / portTICK_RATE_MS);
|
||||
// Interface Mode Control
|
||||
LCD_WRITE_CMD(0xb0);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
// Interface Pixel Format, 16 bits / pixel
|
||||
LCD_WRITE_CMD(0x3A);
|
||||
LCD_WRITE_DATA(0x55); // 5D
|
||||
// PGAMCTRL(Positive Gamma Control)
|
||||
LCD_WRITE_CMD(0xE0);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x1F);
|
||||
LCD_WRITE_DATA(0x1C);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x48);
|
||||
LCD_WRITE_DATA(0x98);
|
||||
LCD_WRITE_DATA(0x37);
|
||||
LCD_WRITE_DATA(0x0A);
|
||||
LCD_WRITE_DATA(0x13);
|
||||
LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x11);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
// NGAMCTRL (Negative Gamma Correction)
|
||||
LCD_WRITE_CMD(0xE1);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x32);
|
||||
LCD_WRITE_DATA(0x2E);
|
||||
LCD_WRITE_DATA(0x0B);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
LCD_WRITE_DATA(0x47);
|
||||
LCD_WRITE_DATA(0x75);
|
||||
LCD_WRITE_DATA(0x37);
|
||||
LCD_WRITE_DATA(0x06);
|
||||
LCD_WRITE_DATA(0x10);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x24);
|
||||
LCD_WRITE_DATA(0x20);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
// Digital Gamma Control 1
|
||||
LCD_WRITE_CMD(0xE2);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x32);
|
||||
LCD_WRITE_DATA(0x2E);
|
||||
LCD_WRITE_DATA(0x0B);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
LCD_WRITE_DATA(0x47);
|
||||
LCD_WRITE_DATA(0x75);
|
||||
LCD_WRITE_DATA(0x37);
|
||||
LCD_WRITE_DATA(0x06);
|
||||
LCD_WRITE_DATA(0x10);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x24);
|
||||
LCD_WRITE_DATA(0x20);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
// Set rotation
|
||||
// setRotation(_rotation);
|
||||
|
||||
// Idle mode control + Power + Frame rate ctrl
|
||||
LCD_WRITE_CMD(0x38);
|
||||
// frame rate ctrl
|
||||
LCD_WRITE_CMD(0xB1);
|
||||
// Frame rate(Hz) (default: 70kHz) /-/ Division Ratio (default: fosc)
|
||||
LCD_WRITE_DATA(0xB0);
|
||||
// Clock per Line (default: 17 clk cycles)
|
||||
LCD_WRITE_DATA(0x11);
|
||||
// Power Control 3 (For Normal Mode)
|
||||
LCD_WRITE_CMD(0xC2);
|
||||
LCD_WRITE_DATA(0x55); // 44
|
||||
|
||||
// Display Inversion Control
|
||||
LCD_WRITE_CMD(0xB4);
|
||||
LCD_WRITE_DATA(0x02); // 2 dot invercion /-/ disabled | 0x12 to enable
|
||||
// Display Function Control
|
||||
LCD_WRITE_CMD(0xB6);
|
||||
LCD_WRITE_DATA(0x02);
|
||||
LCD_WRITE_DATA(0x22);
|
||||
LCD_WRITE_DATA(0x3B);
|
||||
// # Sleep OUT
|
||||
LCD_WRITE_CMD(0x11);
|
||||
vTaskDelay(150 / portTICK_RATE_MS);
|
||||
// Display ON
|
||||
LCD_WRITE_CMD(0x29);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
#ifndef _IOT_ILI9486_H_
|
||||
#define _IOT_ILI9486_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_ili9486_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9486_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,401 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ili9488.h"
|
||||
|
||||
static const char *TAG = "ILI9488";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
|
||||
#define ILI9488_NOP 0x00
|
||||
#define ILI9488_SWRESET 0x01
|
||||
#define ILI9488_RDDID 0x04
|
||||
#define ILI9488_RDDST 0x09
|
||||
|
||||
#define ILI9488_SLPIN 0x10
|
||||
#define ILI9488_SLPOUT 0x11
|
||||
#define ILI9488_PTLON 0x12
|
||||
#define ILI9488_NORON 0x13
|
||||
|
||||
#define ILI9488_RDMODE 0x0A
|
||||
#define ILI9488_RDMADCTL 0x0B
|
||||
#define ILI9488_RDPIXFMT 0x0C
|
||||
#define ILI9488_RDIMGFMT 0x0D
|
||||
#define ILI9488_RDSELFDIAG 0x0F
|
||||
|
||||
#define ILI9488_INVOFF 0x20
|
||||
#define ILI9488_INVON 0x21
|
||||
#define ILI9488_GAMMASET 0x26
|
||||
#define ILI9488_DISPOFF 0x28
|
||||
#define ILI9488_DISPON 0x29
|
||||
|
||||
#define ILI9488_CASET 0x2A
|
||||
#define ILI9488_PASET 0x2B
|
||||
#define ILI9488_RAMWR 0x2C
|
||||
#define ILI9488_RAMRD 0x2E
|
||||
|
||||
#define ILI9488_PTLAR 0x30
|
||||
#define ILI9488_VSCRDEF 0x33
|
||||
#define ILI9488_MADCTL 0x36
|
||||
#define ILI9488_VSCRSADD 0x37
|
||||
#define ILI9488_PIXFMT 0x3A
|
||||
#define ILI9488_RAMWRCONT 0x3C
|
||||
#define ILI9488_RAMRDCONT 0x3E
|
||||
|
||||
#define ILI9488_IMCTR 0xB0
|
||||
#define ILI9488_FRMCTR1 0xB1
|
||||
#define ILI9488_FRMCTR2 0xB2
|
||||
#define ILI9488_FRMCTR3 0xB3
|
||||
#define ILI9488_INVCTR 0xB4
|
||||
#define ILI9488_DFUNCTR 0xB6
|
||||
|
||||
#define ILI9488_PWCTR1 0xC0
|
||||
#define ILI9488_PWCTR2 0xC1
|
||||
#define ILI9488_PWCTR3 0xC2
|
||||
#define ILI9488_PWCTR4 0xC3
|
||||
#define ILI9488_PWCTR5 0xC4
|
||||
#define ILI9488_VMCTR1 0xC5
|
||||
#define ILI9488_VMCTR2 0xC7
|
||||
|
||||
#define ILI9488_RDID1 0xDA
|
||||
#define ILI9488_RDID2 0xDB
|
||||
#define ILI9488_RDID3 0xDC
|
||||
#define ILI9488_RDID4 0xDD
|
||||
|
||||
#define ILI9488_GMCTRP1 0xE0
|
||||
#define ILI9488_GMCTRN1 0xE1
|
||||
#define ILI9488_IMGFUNCT 0xE9
|
||||
|
||||
#define ILI9488_ADJCTR3 0xF7
|
||||
|
||||
#define ILI9488_MAD_RGB 0x08
|
||||
#define ILI9488_MAD_BGR 0x00
|
||||
|
||||
#define ILI9488_MAD_VERTICAL 0x20
|
||||
#define ILI9488_MAD_X_LEFT 0x00
|
||||
#define ILI9488_MAD_X_RIGHT 0x40
|
||||
#define ILI9488_MAD_Y_UP 0x80
|
||||
#define ILI9488_MAD_Y_DOWN 0x00
|
||||
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define LCD_NAME "ILI9488"
|
||||
#define LCD_BPP 16
|
||||
|
||||
#define ILI9488_RESOLUTION_HOR 320
|
||||
#define ILI9488_RESOLUTION_VER 480
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ili9488_default_driver = {
|
||||
.init = lcd_ili9488_init,
|
||||
.deinit = lcd_ili9488_deinit,
|
||||
.set_direction = lcd_ili9488_set_rotation,
|
||||
.set_window = lcd_ili9488_set_window,
|
||||
.write_ram_data = lcd_ili9488_write_ram_data,
|
||||
.draw_pixel = lcd_ili9488_draw_pixel,
|
||||
.draw_bitmap = lcd_ili9488_draw_bitmap,
|
||||
.get_info = lcd_ili9488_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void lcd_ili9488_init_reg(void);
|
||||
|
||||
esp_err_t lcd_ili9488_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ILI9488_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ILI9488_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_ili9488_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_ili9488_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
ret = lcd_ili9488_set_invert(1); /**< ILI9488 setting the reverse color is the normal color */
|
||||
LCD_CHECK(ESP_OK == ret, "Set color invert failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(ILI9488_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_set_invert(bool is_invert)
|
||||
{
|
||||
return LCD_WRITE_CMD(is_invert ? ILI9488_INVON : ILI9488_INVOFF);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ILI9488_RESOLUTION_HOR, ILI9488_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9488_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(ILI9488_PASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9488_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ili9488_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ili9488_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9488_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ili9488_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void lcd_ili9488_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(ILI9488_SWRESET);
|
||||
vTaskDelay(120 / portTICK_RATE_MS);
|
||||
// positive gamma control
|
||||
LCD_WRITE_CMD(ILI9488_GMCTRP1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x09);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x16);
|
||||
LCD_WRITE_DATA(0x0A);
|
||||
LCD_WRITE_DATA(0x3F);
|
||||
LCD_WRITE_DATA(0x78);
|
||||
LCD_WRITE_DATA(0x4C);
|
||||
LCD_WRITE_DATA(0x09);
|
||||
LCD_WRITE_DATA(0x0A);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x16);
|
||||
LCD_WRITE_DATA(0x1A);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
|
||||
// negative gamma control
|
||||
LCD_WRITE_CMD(ILI9488_GMCTRN1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x16);
|
||||
LCD_WRITE_DATA(0x19);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
LCD_WRITE_DATA(0x32);
|
||||
LCD_WRITE_DATA(0x45);
|
||||
LCD_WRITE_DATA(0x46);
|
||||
LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x0E);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x35);
|
||||
LCD_WRITE_DATA(0x37);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
|
||||
// Power Control 1 (Vreg1out, Verg2out)
|
||||
LCD_WRITE_CMD(ILI9488_PWCTR1);
|
||||
LCD_WRITE_DATA(0x17);
|
||||
LCD_WRITE_DATA(0x15);
|
||||
|
||||
// Power Control 2 (VGH,VGL)
|
||||
LCD_WRITE_CMD(ILI9488_PWCTR2);
|
||||
LCD_WRITE_DATA(0x41);
|
||||
// Power Control 3 (Vcom)
|
||||
LCD_WRITE_CMD(ILI9488_VMCTR1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x12);
|
||||
LCD_WRITE_DATA(0x80);
|
||||
|
||||
LCD_WRITE_CMD(ILI9488_IMCTR); LCD_WRITE_DATA(0x80); // Interface Mode Control (SDO NOT USE)
|
||||
|
||||
LCD_WRITE_CMD(ILI9488_PIXFMT); LCD_WRITE_DATA(0x55); // Interface Pixel Format (16 bit)
|
||||
|
||||
LCD_WRITE_CMD(ILI9488_FRMCTR1); LCD_WRITE_DATA(0xA0); // Frame rate (60Hz)
|
||||
LCD_WRITE_CMD(ILI9488_INVCTR); LCD_WRITE_DATA(0x02); // Display Inversion Control (2-dot)
|
||||
LCD_WRITE_CMD(ILI9488_DFUNCTR); // Display Function Control RGB/MCU Interface Control
|
||||
LCD_WRITE_DATA(0x02);
|
||||
LCD_WRITE_DATA(0x02);
|
||||
|
||||
LCD_WRITE_CMD(ILI9488_IMGFUNCT); // Set Image Functio (Disable 24 bit data)
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_CMD(ILI9488_ADJCTR3); // Adjust Control (D7 stream, loose)
|
||||
LCD_WRITE_DATA(0xa9);
|
||||
LCD_WRITE_DATA(0x51);
|
||||
LCD_WRITE_DATA(0x2c);
|
||||
LCD_WRITE_DATA(0x82);
|
||||
|
||||
LCD_WRITE_CMD(ILI9488_SLPOUT); // Exit Sleep
|
||||
LCD_WRITE_CMD(ILI9488_DISPON); // Display on
|
||||
vTaskDelay(120 / portTICK_RATE_MS);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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.
|
||||
#ifndef _IOT_ILI9488_H_
|
||||
#define _IOT_ILI9488_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_ili9488_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_set_invert(bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9488_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,454 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ili9806.h"
|
||||
|
||||
static const char *TAG = "lcd ili9806";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define ILI9806_CASET 0x2A
|
||||
#define ILI9806_RASET 0x2B
|
||||
#define ILI9806_RAMWR 0x2C
|
||||
#define ILI9806_MADCTL 0x36
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define LCD_NAME "ILI9806"
|
||||
#define LCD_BPP 16
|
||||
|
||||
/** ILI9806 can select different resolution */
|
||||
#define ILI9806_RESOLUTION_HOR 480 //fixed
|
||||
#define ILI9806_RESOLUTION_VER 854 //optional
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ili9806_default_driver = {
|
||||
.init = lcd_ili9806_init,
|
||||
.deinit = lcd_ili9806_deinit,
|
||||
.set_direction = lcd_ili9806_set_rotation,
|
||||
.set_window = lcd_ili9806_set_window,
|
||||
.write_ram_data = lcd_ili9806_write_ram_data,
|
||||
.draw_pixel = lcd_ili9806_draw_pixel,
|
||||
.draw_bitmap = lcd_ili9806_draw_bitmap,
|
||||
.get_info = lcd_ili9806_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void lcd_ili9806_init_reg(void);
|
||||
|
||||
|
||||
esp_err_t lcd_ili9806_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ILI9806_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ILI9806_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_ili9806_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_ili9806_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data &= ~MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(ILI9806_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ILI9806_RESOLUTION_HOR, ILI9806_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9806_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(ILI9806_RASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(ILI9806_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t lcd_ili9806_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ili9806_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ili9806_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ili9806_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ili9806_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void lcd_ili9806_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x01); // Software Reset
|
||||
vTaskDelay(50 / portTICK_RATE_MS);
|
||||
LCD_WRITE_CMD(0xFF); // EXTC Command Set enable register
|
||||
LCD_WRITE_DATA(0xFF);
|
||||
LCD_WRITE_DATA(0x98);
|
||||
LCD_WRITE_DATA(0x06);
|
||||
|
||||
LCD_WRITE_CMD(0xBA); // SPI Interface Setting
|
||||
LCD_WRITE_DATA(0xE0);
|
||||
|
||||
LCD_WRITE_CMD(0xBC); // GIP 1
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x63);
|
||||
LCD_WRITE_DATA(0x69);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0x1B);
|
||||
LCD_WRITE_DATA(0x11);
|
||||
LCD_WRITE_DATA(0x70);
|
||||
LCD_WRITE_DATA(0x73);
|
||||
LCD_WRITE_DATA(0xFF);
|
||||
LCD_WRITE_DATA(0xFF);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
LCD_WRITE_DATA(0x09);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0xEE);
|
||||
LCD_WRITE_DATA(0xE2);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0xC1);
|
||||
|
||||
LCD_WRITE_CMD(0xBD); // GIP 2
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0x23);
|
||||
LCD_WRITE_DATA(0x45);
|
||||
LCD_WRITE_DATA(0x67);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0x23);
|
||||
LCD_WRITE_DATA(0x45);
|
||||
LCD_WRITE_DATA(0x67);
|
||||
|
||||
LCD_WRITE_CMD(0xBE); // GIP 3
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x22);
|
||||
LCD_WRITE_DATA(0x27);
|
||||
LCD_WRITE_DATA(0x6A);
|
||||
LCD_WRITE_DATA(0xBC);
|
||||
LCD_WRITE_DATA(0xD8);
|
||||
LCD_WRITE_DATA(0x92);
|
||||
LCD_WRITE_DATA(0x22);
|
||||
LCD_WRITE_DATA(0x22);
|
||||
|
||||
LCD_WRITE_CMD(0xC7); // Vcom
|
||||
LCD_WRITE_DATA(0x1E);
|
||||
|
||||
LCD_WRITE_CMD(0xED); // EN_volt_reg
|
||||
LCD_WRITE_DATA(0x7F);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0xC0); // Power Control 1
|
||||
LCD_WRITE_DATA(0xE3);
|
||||
LCD_WRITE_DATA(0x0B);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0xFC);
|
||||
LCD_WRITE_DATA(0x08);
|
||||
|
||||
LCD_WRITE_CMD(0xDF); // Engineering Setting
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x02);
|
||||
|
||||
LCD_WRITE_CMD(0xF3); // DVDD Voltage Setting
|
||||
LCD_WRITE_DATA(0x74);
|
||||
|
||||
LCD_WRITE_CMD(0xB4); // Display Inversion Control
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0xF7); // Panel Resolution Selection Set.
|
||||
switch (ILI9806_RESOLUTION_VER) {
|
||||
case 864:
|
||||
LCD_WRITE_DATA(0x80); // set to 480x864
|
||||
break;
|
||||
case 854:
|
||||
LCD_WRITE_DATA(0x81); // set to 480x854
|
||||
break;
|
||||
case 800:
|
||||
LCD_WRITE_DATA(0x82); // set to 480x800
|
||||
break;
|
||||
case 640:
|
||||
LCD_WRITE_DATA(0x83); // set to 480x640
|
||||
break;
|
||||
case 720:
|
||||
LCD_WRITE_DATA(0x84); // set to 480x720
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported resolution, use default [480x865]");
|
||||
break;
|
||||
}
|
||||
|
||||
LCD_WRITE_CMD(0xB1); // Frame Rate
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x10);
|
||||
LCD_WRITE_DATA(0x14);
|
||||
|
||||
LCD_WRITE_CMD(0xF1); // Panel Timing Control
|
||||
LCD_WRITE_DATA(0x29);
|
||||
LCD_WRITE_DATA(0x8A);
|
||||
LCD_WRITE_DATA(0x07);
|
||||
|
||||
LCD_WRITE_CMD(0xF2); //Panel Timing Control
|
||||
LCD_WRITE_DATA(0x40);
|
||||
LCD_WRITE_DATA(0xD2);
|
||||
LCD_WRITE_DATA(0x50);
|
||||
LCD_WRITE_DATA(0x28);
|
||||
|
||||
LCD_WRITE_CMD(0xC1); // Power Control 2
|
||||
LCD_WRITE_DATA(0x17);
|
||||
LCD_WRITE_DATA(0X85);
|
||||
LCD_WRITE_DATA(0x85);
|
||||
LCD_WRITE_DATA(0x20);
|
||||
|
||||
LCD_WRITE_CMD(0xE0);
|
||||
LCD_WRITE_DATA(0x00); //P1
|
||||
LCD_WRITE_DATA(0x0C); //P2
|
||||
LCD_WRITE_DATA(0x15); //P3
|
||||
LCD_WRITE_DATA(0x0D); //P4
|
||||
LCD_WRITE_DATA(0x0F); //P5
|
||||
LCD_WRITE_DATA(0x0C); //P6
|
||||
LCD_WRITE_DATA(0x07); //P7
|
||||
LCD_WRITE_DATA(0x05); //P8
|
||||
LCD_WRITE_DATA(0x07); //P9
|
||||
LCD_WRITE_DATA(0x0B); //P10
|
||||
LCD_WRITE_DATA(0x10); //P11
|
||||
LCD_WRITE_DATA(0x10); //P12
|
||||
LCD_WRITE_DATA(0x0D); //P13
|
||||
LCD_WRITE_DATA(0x17); //P14
|
||||
LCD_WRITE_DATA(0x0F); //P15
|
||||
LCD_WRITE_DATA(0x00); //P16
|
||||
|
||||
LCD_WRITE_CMD(0xE1);
|
||||
LCD_WRITE_DATA(0x00); //P1
|
||||
LCD_WRITE_DATA(0x0D); //P2
|
||||
LCD_WRITE_DATA(0x15); //P3
|
||||
LCD_WRITE_DATA(0x0E); //P4
|
||||
LCD_WRITE_DATA(0x10); //P5
|
||||
LCD_WRITE_DATA(0x0D); //P6
|
||||
LCD_WRITE_DATA(0x08); //P7
|
||||
LCD_WRITE_DATA(0x06); //P8
|
||||
LCD_WRITE_DATA(0x07); //P9
|
||||
LCD_WRITE_DATA(0x0C); //P10
|
||||
LCD_WRITE_DATA(0x11); //P11
|
||||
LCD_WRITE_DATA(0x11); //P12
|
||||
LCD_WRITE_DATA(0x0E); //P13
|
||||
LCD_WRITE_DATA(0x17); //P14
|
||||
LCD_WRITE_DATA(0x0F); //P15
|
||||
LCD_WRITE_DATA(0x00); //P16
|
||||
|
||||
LCD_WRITE_CMD(0x35); //Tearing Effect ON
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0x36); //Tearing Effect ON
|
||||
LCD_WRITE_DATA(0x60);
|
||||
|
||||
// LCD_WRITE_CMD(0x38);
|
||||
|
||||
LCD_WRITE_CMD(0x3A);
|
||||
LCD_WRITE_DATA(0x55);
|
||||
|
||||
LCD_WRITE_CMD(0x11); //Exit Sleep
|
||||
vTaskDelay(10 / portTICK_RATE_MS);
|
||||
LCD_WRITE_CMD(0x29); // Display On
|
||||
|
||||
LCD_WRITE_CMD(0x36);
|
||||
LCD_WRITE_DATA((1 << 6) | (1 << 5));
|
||||
|
||||
LCD_WRITE_CMD(0x2A);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x55);
|
||||
|
||||
LCD_WRITE_CMD(0x2B);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_DATA(0xDF);
|
||||
LCD_WRITE_CMD(0x2C);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
#ifndef _IOT_ILI9806_H_
|
||||
#define _IOT_ILI9806_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_ili9806_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ili9806_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,639 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "nt35510.h"
|
||||
|
||||
static const char *TAG = "lcd nt35510";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "NT35510"
|
||||
#define LCD_BPP 16
|
||||
|
||||
/**
|
||||
* NT35510 can select different resolution, but I can't find the way
|
||||
*/
|
||||
#define NT35510_RESOLUTION_HOR 480
|
||||
#define NT35510_RESOLUTION_VER 800
|
||||
|
||||
#define NT35510_CASET 0x2A00
|
||||
#define NT35510_RASET 0x2B00
|
||||
#define NT35510_RAMWR 0x2C00
|
||||
#define NT35510_MADCTL 0x3600
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_nt35510_default_driver = {
|
||||
.init = lcd_nt35510_init,
|
||||
.deinit = lcd_nt35510_deinit,
|
||||
.set_direction = lcd_nt35510_set_rotation,
|
||||
.set_window = lcd_nt35510_set_window,
|
||||
.write_ram_data = lcd_nt35510_write_ram_data,
|
||||
.draw_pixel = lcd_nt35510_draw_pixel,
|
||||
.draw_bitmap = lcd_nt35510_draw_bitmap,
|
||||
.get_info = lcd_nt35510_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void lcd_nt35510_init_reg(void);
|
||||
|
||||
esp_err_t lcd_nt35510_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= NT35510_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= NT35510_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_nt35510_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_nt35510_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data &= ~MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(NT35510_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, NT35510_RESOLUTION_HOR, NT35510_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_REG(NT35510_CASET, x0 >> 8);
|
||||
ret |= LCD_WRITE_REG(NT35510_CASET + 1, x0 & 0xff);
|
||||
ret |= LCD_WRITE_REG(NT35510_CASET + 2, x1 >> 8);
|
||||
ret |= LCD_WRITE_REG(NT35510_CASET + 3, x1 & 0xff);
|
||||
ret |= LCD_WRITE_REG(NT35510_RASET, y0 >> 8);
|
||||
ret |= LCD_WRITE_REG(NT35510_RASET + 1, y0 & 0xff);
|
||||
ret |= LCD_WRITE_REG(NT35510_RASET + 2, y1 >> 8);
|
||||
ret |= LCD_WRITE_REG(NT35510_RASET + 3, y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(NT35510_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_nt35510_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_nt35510_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_nt35510_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_nt35510_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void lcd_nt35510_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x0100); // Software Reset
|
||||
vTaskDelay(10 / portTICK_RATE_MS);
|
||||
LCD_WRITE_CMD(0x1200);
|
||||
LCD_WRITE_REG(0xf000, 0x0055);
|
||||
LCD_WRITE_REG(0xf001, 0x00aa);
|
||||
LCD_WRITE_REG(0xf002, 0x0052);
|
||||
LCD_WRITE_REG(0xf003, 0x0008);
|
||||
LCD_WRITE_REG(0xf004, 0x0001);
|
||||
|
||||
LCD_WRITE_REG(0xbc01, 0x0086);
|
||||
LCD_WRITE_REG(0xbc02, 0x006a);
|
||||
LCD_WRITE_REG(0xbd01, 0x0086);
|
||||
LCD_WRITE_REG(0xbd02, 0x006a);
|
||||
LCD_WRITE_REG(0xbe01, 0x0067);
|
||||
|
||||
LCD_WRITE_REG(0xd100, 0x0000);
|
||||
LCD_WRITE_REG(0xd101, 0x005d);
|
||||
LCD_WRITE_REG(0xd102, 0x0000);
|
||||
LCD_WRITE_REG(0xd103, 0x006b);
|
||||
LCD_WRITE_REG(0xd104, 0x0000);
|
||||
LCD_WRITE_REG(0xd105, 0x0084);
|
||||
LCD_WRITE_REG(0xd106, 0x0000);
|
||||
LCD_WRITE_REG(0xd107, 0x009c);
|
||||
LCD_WRITE_REG(0xd108, 0x0000);
|
||||
LCD_WRITE_REG(0xd109, 0x00b1);
|
||||
LCD_WRITE_REG(0xd10a, 0x0000);
|
||||
LCD_WRITE_REG(0xd10b, 0x00d9);
|
||||
LCD_WRITE_REG(0xd10c, 0x0000);
|
||||
LCD_WRITE_REG(0xd10d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd10e, 0x0001);
|
||||
LCD_WRITE_REG(0xd10f, 0x0038);
|
||||
LCD_WRITE_REG(0xd110, 0x0001);
|
||||
LCD_WRITE_REG(0xd111, 0x0068);
|
||||
LCD_WRITE_REG(0xd112, 0x0001);
|
||||
LCD_WRITE_REG(0xd113, 0x00b9);
|
||||
LCD_WRITE_REG(0xd114, 0x0001);
|
||||
LCD_WRITE_REG(0xd115, 0x00fb);
|
||||
LCD_WRITE_REG(0xd116, 0x0002);
|
||||
LCD_WRITE_REG(0xd117, 0x0063);
|
||||
LCD_WRITE_REG(0xd118, 0x0002);
|
||||
LCD_WRITE_REG(0xd119, 0x00b9);
|
||||
LCD_WRITE_REG(0xd11a, 0x0002);
|
||||
LCD_WRITE_REG(0xd11b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd11c, 0x0003);
|
||||
LCD_WRITE_REG(0xd11d, 0x0003);
|
||||
LCD_WRITE_REG(0xd11e, 0x0003);
|
||||
LCD_WRITE_REG(0xd11f, 0x0046);
|
||||
LCD_WRITE_REG(0xd120, 0x0003);
|
||||
LCD_WRITE_REG(0xd121, 0x0069);
|
||||
LCD_WRITE_REG(0xd122, 0x0003);
|
||||
LCD_WRITE_REG(0xd123, 0x008f);
|
||||
LCD_WRITE_REG(0xd124, 0x0003);
|
||||
LCD_WRITE_REG(0xd125, 0x00a4);
|
||||
LCD_WRITE_REG(0xd126, 0x0003);
|
||||
LCD_WRITE_REG(0xd127, 0x00b9);
|
||||
LCD_WRITE_REG(0xd128, 0x0003);
|
||||
LCD_WRITE_REG(0xd129, 0x00c7);
|
||||
LCD_WRITE_REG(0xd12a, 0x0003);
|
||||
LCD_WRITE_REG(0xd12b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd12c, 0x0003);
|
||||
LCD_WRITE_REG(0xd12d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd12e, 0x0003);
|
||||
LCD_WRITE_REG(0xd12f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd130, 0x0003);
|
||||
LCD_WRITE_REG(0xd131, 0x00cb);
|
||||
LCD_WRITE_REG(0xd132, 0x0003);
|
||||
LCD_WRITE_REG(0xd133, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xd200, 0x0000);
|
||||
LCD_WRITE_REG(0xd201, 0x005d);
|
||||
LCD_WRITE_REG(0xd202, 0x0000);
|
||||
LCD_WRITE_REG(0xd203, 0x006b);
|
||||
LCD_WRITE_REG(0xd204, 0x0000);
|
||||
LCD_WRITE_REG(0xd205, 0x0084);
|
||||
LCD_WRITE_REG(0xd206, 0x0000);
|
||||
LCD_WRITE_REG(0xd207, 0x009c);
|
||||
LCD_WRITE_REG(0xd208, 0x0000);
|
||||
LCD_WRITE_REG(0xd209, 0x00b1);
|
||||
LCD_WRITE_REG(0xd20a, 0x0000);
|
||||
LCD_WRITE_REG(0xd20b, 0x00d9);
|
||||
LCD_WRITE_REG(0xd20c, 0x0000);
|
||||
LCD_WRITE_REG(0xd20d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd20e, 0x0001);
|
||||
LCD_WRITE_REG(0xd20f, 0x0038);
|
||||
LCD_WRITE_REG(0xd210, 0x0001);
|
||||
LCD_WRITE_REG(0xd211, 0x0068);
|
||||
LCD_WRITE_REG(0xd212, 0x0001);
|
||||
LCD_WRITE_REG(0xd213, 0x00b9);
|
||||
LCD_WRITE_REG(0xd214, 0x0001);
|
||||
LCD_WRITE_REG(0xd215, 0x00fb);
|
||||
LCD_WRITE_REG(0xd216, 0x0002);
|
||||
LCD_WRITE_REG(0xd217, 0x0063);
|
||||
LCD_WRITE_REG(0xd218, 0x0002);
|
||||
LCD_WRITE_REG(0xd219, 0x00b9);
|
||||
LCD_WRITE_REG(0xd21a, 0x0002);
|
||||
LCD_WRITE_REG(0xd21b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd21c, 0x0003);
|
||||
LCD_WRITE_REG(0xd21d, 0x0003);
|
||||
LCD_WRITE_REG(0xd21e, 0x0003);
|
||||
LCD_WRITE_REG(0xd21f, 0x0046);
|
||||
LCD_WRITE_REG(0xd220, 0x0003);
|
||||
LCD_WRITE_REG(0xd221, 0x0069);
|
||||
LCD_WRITE_REG(0xd222, 0x0003);
|
||||
LCD_WRITE_REG(0xd223, 0x008f);
|
||||
LCD_WRITE_REG(0xd224, 0x0003);
|
||||
LCD_WRITE_REG(0xd225, 0x00a4);
|
||||
LCD_WRITE_REG(0xd226, 0x0003);
|
||||
LCD_WRITE_REG(0xd227, 0x00b9);
|
||||
LCD_WRITE_REG(0xd228, 0x0003);
|
||||
LCD_WRITE_REG(0xd229, 0x00c7);
|
||||
LCD_WRITE_REG(0xd22a, 0x0003);
|
||||
LCD_WRITE_REG(0xd22b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd22c, 0x0003);
|
||||
LCD_WRITE_REG(0xd22d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd22e, 0x0003);
|
||||
LCD_WRITE_REG(0xd22f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd230, 0x0003);
|
||||
LCD_WRITE_REG(0xd231, 0x00cb);
|
||||
LCD_WRITE_REG(0xd232, 0x0003);
|
||||
LCD_WRITE_REG(0xd233, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xd300, 0x0000);
|
||||
LCD_WRITE_REG(0xd301, 0x005d);
|
||||
LCD_WRITE_REG(0xd302, 0x0000);
|
||||
LCD_WRITE_REG(0xd303, 0x006b);
|
||||
LCD_WRITE_REG(0xd304, 0x0000);
|
||||
LCD_WRITE_REG(0xd305, 0x0084);
|
||||
LCD_WRITE_REG(0xd306, 0x0000);
|
||||
LCD_WRITE_REG(0xd307, 0x009c);
|
||||
LCD_WRITE_REG(0xd308, 0x0000);
|
||||
LCD_WRITE_REG(0xd309, 0x00b1);
|
||||
LCD_WRITE_REG(0xd30a, 0x0000);
|
||||
LCD_WRITE_REG(0xd30b, 0x00d9);
|
||||
LCD_WRITE_REG(0xd30c, 0x0000);
|
||||
LCD_WRITE_REG(0xd30d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd30e, 0x0001);
|
||||
LCD_WRITE_REG(0xd30f, 0x0038);
|
||||
LCD_WRITE_REG(0xd310, 0x0001);
|
||||
LCD_WRITE_REG(0xd311, 0x0068);
|
||||
LCD_WRITE_REG(0xd312, 0x0001);
|
||||
LCD_WRITE_REG(0xd313, 0x00b9);
|
||||
LCD_WRITE_REG(0xd314, 0x0001);
|
||||
LCD_WRITE_REG(0xd315, 0x00fb);
|
||||
LCD_WRITE_REG(0xd316, 0x0002);
|
||||
LCD_WRITE_REG(0xd317, 0x0063);
|
||||
LCD_WRITE_REG(0xd318, 0x0002);
|
||||
LCD_WRITE_REG(0xd319, 0x00b9);
|
||||
LCD_WRITE_REG(0xd31a, 0x0002);
|
||||
LCD_WRITE_REG(0xd31b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd31c, 0x0003);
|
||||
LCD_WRITE_REG(0xd31d, 0x0003);
|
||||
LCD_WRITE_REG(0xd31e, 0x0003);
|
||||
LCD_WRITE_REG(0xd31f, 0x0046);
|
||||
LCD_WRITE_REG(0xd320, 0x0003);
|
||||
LCD_WRITE_REG(0xd321, 0x0069);
|
||||
LCD_WRITE_REG(0xd322, 0x0003);
|
||||
LCD_WRITE_REG(0xd323, 0x008f);
|
||||
LCD_WRITE_REG(0xd324, 0x0003);
|
||||
LCD_WRITE_REG(0xd325, 0x00a4);
|
||||
LCD_WRITE_REG(0xd326, 0x0003);
|
||||
LCD_WRITE_REG(0xd327, 0x00b9);
|
||||
LCD_WRITE_REG(0xd328, 0x0003);
|
||||
LCD_WRITE_REG(0xd329, 0x00c7);
|
||||
LCD_WRITE_REG(0xd32a, 0x0003);
|
||||
LCD_WRITE_REG(0xd32b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd32c, 0x0003);
|
||||
LCD_WRITE_REG(0xd32d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd32e, 0x0003);
|
||||
LCD_WRITE_REG(0xd32f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd330, 0x0003);
|
||||
LCD_WRITE_REG(0xd331, 0x00cb);
|
||||
LCD_WRITE_REG(0xd332, 0x0003);
|
||||
LCD_WRITE_REG(0xd333, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xd400, 0x0000);
|
||||
LCD_WRITE_REG(0xd401, 0x005d);
|
||||
LCD_WRITE_REG(0xd402, 0x0000);
|
||||
LCD_WRITE_REG(0xd403, 0x006b);
|
||||
LCD_WRITE_REG(0xd404, 0x0000);
|
||||
LCD_WRITE_REG(0xd405, 0x0084);
|
||||
LCD_WRITE_REG(0xd406, 0x0000);
|
||||
LCD_WRITE_REG(0xd407, 0x009c);
|
||||
LCD_WRITE_REG(0xd408, 0x0000);
|
||||
LCD_WRITE_REG(0xd409, 0x00b1);
|
||||
LCD_WRITE_REG(0xd40a, 0x0000);
|
||||
LCD_WRITE_REG(0xd40b, 0x00d9);
|
||||
LCD_WRITE_REG(0xd40c, 0x0000);
|
||||
LCD_WRITE_REG(0xd40d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd40e, 0x0001);
|
||||
LCD_WRITE_REG(0xd40f, 0x0038);
|
||||
LCD_WRITE_REG(0xd410, 0x0001);
|
||||
LCD_WRITE_REG(0xd411, 0x0068);
|
||||
LCD_WRITE_REG(0xd412, 0x0001);
|
||||
LCD_WRITE_REG(0xd413, 0x00b9);
|
||||
LCD_WRITE_REG(0xd414, 0x0001);
|
||||
LCD_WRITE_REG(0xd415, 0x00fb);
|
||||
LCD_WRITE_REG(0xd416, 0x0002);
|
||||
LCD_WRITE_REG(0xd417, 0x0063);
|
||||
LCD_WRITE_REG(0xd418, 0x0002);
|
||||
LCD_WRITE_REG(0xd419, 0x00b9);
|
||||
LCD_WRITE_REG(0xd41a, 0x0002);
|
||||
LCD_WRITE_REG(0xd41b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd41c, 0x0003);
|
||||
LCD_WRITE_REG(0xd41d, 0x0003);
|
||||
LCD_WRITE_REG(0xd41e, 0x0003);
|
||||
LCD_WRITE_REG(0xd41f, 0x0046);
|
||||
LCD_WRITE_REG(0xd420, 0x0003);
|
||||
LCD_WRITE_REG(0xd421, 0x0069);
|
||||
LCD_WRITE_REG(0xd422, 0x0003);
|
||||
LCD_WRITE_REG(0xd423, 0x008f);
|
||||
LCD_WRITE_REG(0xd424, 0x0003);
|
||||
LCD_WRITE_REG(0xd425, 0x00a4);
|
||||
LCD_WRITE_REG(0xd426, 0x0003);
|
||||
LCD_WRITE_REG(0xd427, 0x00b9);
|
||||
LCD_WRITE_REG(0xd428, 0x0003);
|
||||
LCD_WRITE_REG(0xd429, 0x00c7);
|
||||
LCD_WRITE_REG(0xd42a, 0x0003);
|
||||
LCD_WRITE_REG(0xd42b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd42c, 0x0003);
|
||||
LCD_WRITE_REG(0xd42d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd42e, 0x0003);
|
||||
LCD_WRITE_REG(0xd42f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd430, 0x0003);
|
||||
LCD_WRITE_REG(0xd431, 0x00cb);
|
||||
LCD_WRITE_REG(0xd432, 0x0003);
|
||||
LCD_WRITE_REG(0xd433, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xd500, 0x0000);
|
||||
LCD_WRITE_REG(0xd501, 0x005d);
|
||||
LCD_WRITE_REG(0xd502, 0x0000);
|
||||
LCD_WRITE_REG(0xd503, 0x006b);
|
||||
LCD_WRITE_REG(0xd504, 0x0000);
|
||||
LCD_WRITE_REG(0xd505, 0x0084);
|
||||
LCD_WRITE_REG(0xd506, 0x0000);
|
||||
LCD_WRITE_REG(0xd507, 0x009c);
|
||||
LCD_WRITE_REG(0xd508, 0x0000);
|
||||
LCD_WRITE_REG(0xd509, 0x00b1);
|
||||
LCD_WRITE_REG(0xd50a, 0x0000);
|
||||
LCD_WRITE_REG(0xd50b, 0x00D9);
|
||||
LCD_WRITE_REG(0xd50c, 0x0000);
|
||||
LCD_WRITE_REG(0xd50d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd50e, 0x0001);
|
||||
LCD_WRITE_REG(0xd50f, 0x0038);
|
||||
LCD_WRITE_REG(0xd510, 0x0001);
|
||||
LCD_WRITE_REG(0xd511, 0x0068);
|
||||
LCD_WRITE_REG(0xd512, 0x0001);
|
||||
LCD_WRITE_REG(0xd513, 0x00b9);
|
||||
LCD_WRITE_REG(0xd514, 0x0001);
|
||||
LCD_WRITE_REG(0xd515, 0x00fb);
|
||||
LCD_WRITE_REG(0xd516, 0x0002);
|
||||
LCD_WRITE_REG(0xd517, 0x0063);
|
||||
LCD_WRITE_REG(0xd518, 0x0002);
|
||||
LCD_WRITE_REG(0xd519, 0x00b9);
|
||||
LCD_WRITE_REG(0xd51a, 0x0002);
|
||||
LCD_WRITE_REG(0xd51b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd51c, 0x0003);
|
||||
LCD_WRITE_REG(0xd51d, 0x0003);
|
||||
LCD_WRITE_REG(0xd51e, 0x0003);
|
||||
LCD_WRITE_REG(0xd51f, 0x0046);
|
||||
LCD_WRITE_REG(0xd520, 0x0003);
|
||||
LCD_WRITE_REG(0xd521, 0x0069);
|
||||
LCD_WRITE_REG(0xd522, 0x0003);
|
||||
LCD_WRITE_REG(0xd523, 0x008f);
|
||||
LCD_WRITE_REG(0xd524, 0x0003);
|
||||
LCD_WRITE_REG(0xd525, 0x00a4);
|
||||
LCD_WRITE_REG(0xd526, 0x0003);
|
||||
LCD_WRITE_REG(0xd527, 0x00b9);
|
||||
LCD_WRITE_REG(0xd528, 0x0003);
|
||||
LCD_WRITE_REG(0xd529, 0x00c7);
|
||||
LCD_WRITE_REG(0xd52a, 0x0003);
|
||||
LCD_WRITE_REG(0xd52b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd52c, 0x0003);
|
||||
LCD_WRITE_REG(0xd52d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd52e, 0x0003);
|
||||
LCD_WRITE_REG(0xd52f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd530, 0x0003);
|
||||
LCD_WRITE_REG(0xd531, 0x00cb);
|
||||
LCD_WRITE_REG(0xd532, 0x0003);
|
||||
LCD_WRITE_REG(0xd533, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xd600, 0x0000);
|
||||
LCD_WRITE_REG(0xd601, 0x005d);
|
||||
LCD_WRITE_REG(0xd602, 0x0000);
|
||||
LCD_WRITE_REG(0xd603, 0x006b);
|
||||
LCD_WRITE_REG(0xd604, 0x0000);
|
||||
LCD_WRITE_REG(0xd605, 0x0084);
|
||||
LCD_WRITE_REG(0xd606, 0x0000);
|
||||
LCD_WRITE_REG(0xd607, 0x009c);
|
||||
LCD_WRITE_REG(0xd608, 0x0000);
|
||||
LCD_WRITE_REG(0xd609, 0x00b1);
|
||||
LCD_WRITE_REG(0xd60a, 0x0000);
|
||||
LCD_WRITE_REG(0xd60b, 0x00d9);
|
||||
LCD_WRITE_REG(0xd60c, 0x0000);
|
||||
LCD_WRITE_REG(0xd60d, 0x00fd);
|
||||
LCD_WRITE_REG(0xd60e, 0x0001);
|
||||
LCD_WRITE_REG(0xd60f, 0x0038);
|
||||
LCD_WRITE_REG(0xd610, 0x0001);
|
||||
LCD_WRITE_REG(0xd611, 0x0068);
|
||||
LCD_WRITE_REG(0xd612, 0x0001);
|
||||
LCD_WRITE_REG(0xd613, 0x00b9);
|
||||
LCD_WRITE_REG(0xd614, 0x0001);
|
||||
LCD_WRITE_REG(0xd615, 0x00fb);
|
||||
LCD_WRITE_REG(0xd616, 0x0002);
|
||||
LCD_WRITE_REG(0xd617, 0x0063);
|
||||
LCD_WRITE_REG(0xd618, 0x0002);
|
||||
LCD_WRITE_REG(0xd619, 0x00b9);
|
||||
LCD_WRITE_REG(0xd61a, 0x0002);
|
||||
LCD_WRITE_REG(0xd61b, 0x00bb);
|
||||
LCD_WRITE_REG(0xd61c, 0x0003);
|
||||
LCD_WRITE_REG(0xd61d, 0x0003);
|
||||
LCD_WRITE_REG(0xd61e, 0x0003);
|
||||
LCD_WRITE_REG(0xd61f, 0x0046);
|
||||
LCD_WRITE_REG(0xd620, 0x0003);
|
||||
LCD_WRITE_REG(0xd621, 0x0069);
|
||||
LCD_WRITE_REG(0xd622, 0x0003);
|
||||
LCD_WRITE_REG(0xd623, 0x008f);
|
||||
LCD_WRITE_REG(0xd624, 0x0003);
|
||||
LCD_WRITE_REG(0xd625, 0x00a4);
|
||||
LCD_WRITE_REG(0xd626, 0x0003);
|
||||
LCD_WRITE_REG(0xd627, 0x00b9);
|
||||
LCD_WRITE_REG(0xd628, 0x0003);
|
||||
LCD_WRITE_REG(0xd629, 0x00c7);
|
||||
LCD_WRITE_REG(0xd62a, 0x0003);
|
||||
LCD_WRITE_REG(0xd62b, 0x00c9);
|
||||
LCD_WRITE_REG(0xd62c, 0x0003);
|
||||
LCD_WRITE_REG(0xd62d, 0x00cb);
|
||||
LCD_WRITE_REG(0xd62e, 0x0003);
|
||||
LCD_WRITE_REG(0xd62f, 0x00cb);
|
||||
LCD_WRITE_REG(0xd630, 0x0003);
|
||||
LCD_WRITE_REG(0xd631, 0x00cb);
|
||||
LCD_WRITE_REG(0xd632, 0x0003);
|
||||
LCD_WRITE_REG(0xd633, 0x00cc);
|
||||
|
||||
LCD_WRITE_REG(0xba00, 0x0024);
|
||||
LCD_WRITE_REG(0xba01, 0x0024);
|
||||
LCD_WRITE_REG(0xba02, 0x0024);
|
||||
|
||||
LCD_WRITE_REG(0xb900, 0x0024);
|
||||
LCD_WRITE_REG(0xb901, 0x0024);
|
||||
LCD_WRITE_REG(0xb902, 0x0024);
|
||||
|
||||
LCD_WRITE_REG(0xf000, 0x0055);
|
||||
LCD_WRITE_REG(0xf001, 0x00aa);
|
||||
LCD_WRITE_REG(0xf002, 0x0052);
|
||||
LCD_WRITE_REG(0xf003, 0x0008);
|
||||
LCD_WRITE_REG(0xf004, 0x0000);
|
||||
|
||||
LCD_WRITE_REG(0xb100, 0x00cc);
|
||||
LCD_WRITE_REG(0xB500, 0x0050);
|
||||
|
||||
LCD_WRITE_REG(0xbc00, 0x0005);
|
||||
LCD_WRITE_REG(0xbc01, 0x0005);
|
||||
LCD_WRITE_REG(0xbc02, 0x0005);
|
||||
|
||||
LCD_WRITE_REG(0xb800, 0x0001);
|
||||
LCD_WRITE_REG(0xb801, 0x0003);
|
||||
LCD_WRITE_REG(0xb802, 0x0003);
|
||||
LCD_WRITE_REG(0xb803, 0x0003);
|
||||
|
||||
LCD_WRITE_REG(0xbd02, 0x0007);
|
||||
LCD_WRITE_REG(0xbd03, 0x0031);
|
||||
LCD_WRITE_REG(0xbe02, 0x0007);
|
||||
LCD_WRITE_REG(0xbe03, 0x0031);
|
||||
LCD_WRITE_REG(0xbf02, 0x0007);
|
||||
LCD_WRITE_REG(0xbf03, 0x0031);
|
||||
|
||||
LCD_WRITE_REG(0xff00, 0x00aa);
|
||||
LCD_WRITE_REG(0xff01, 0x0055);
|
||||
LCD_WRITE_REG(0xff02, 0x0025);
|
||||
LCD_WRITE_REG(0xff03, 0x0001);
|
||||
|
||||
LCD_WRITE_REG(0xf304, 0x0011);
|
||||
LCD_WRITE_REG(0xf306, 0x0010);
|
||||
LCD_WRITE_REG(0xf308, 0x0000);
|
||||
|
||||
LCD_WRITE_REG(0x3500, 0x0000);
|
||||
LCD_WRITE_REG(0x3A00, 0x0005);
|
||||
//Display On
|
||||
LCD_WRITE_CMD(0x2900);
|
||||
// Out sleep
|
||||
LCD_WRITE_CMD(0x1100);
|
||||
// Write continue
|
||||
LCD_WRITE_CMD(0x2C00);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
#ifndef _IOT_NT35510_H_
|
||||
#define _IOT_NT35510_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_nt35510_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_nt35510_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _IOT_NT35510_H_ */
|
||||
@@ -0,0 +1,686 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "rm68120.h"
|
||||
|
||||
static const char *TAG = "lcd rm68120";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "RM68120"
|
||||
#define LCD_BPP 16
|
||||
|
||||
/**
|
||||
* RM68120 can select different resolution, but I can't find the way
|
||||
*/
|
||||
#define RM68120_RESOLUTION_HOR 480
|
||||
#define RM68120_RESOLUTION_VER 800
|
||||
|
||||
#define RM68120_CASET 0x2A00
|
||||
#define RM68120_RASET 0x2B00
|
||||
#define RM68120_RAMWR 0x2C00
|
||||
#define RM68120_MADCTL 0x3600
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_rm68120_default_driver = {
|
||||
.init = lcd_rm68120_init,
|
||||
.deinit = lcd_rm68120_deinit,
|
||||
.set_direction = lcd_rm68120_set_rotation,
|
||||
.set_window = lcd_rm68120_set_window,
|
||||
.write_ram_data = lcd_rm68120_write_ram_data,
|
||||
.draw_pixel = lcd_rm68120_draw_pixel,
|
||||
.draw_bitmap = lcd_rm68120_draw_bitmap,
|
||||
.get_info = lcd_rm68120_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void lcd_rm68120_init_reg(void);
|
||||
|
||||
esp_err_t lcd_rm68120_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= RM68120_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= RM68120_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_rm68120_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_rm68120_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data &= ~MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(RM68120_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, RM68120_RESOLUTION_HOR, RM68120_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_REG(RM68120_CASET, x0 >> 8);
|
||||
ret |= LCD_WRITE_REG(RM68120_CASET + 1, x0 & 0xff);
|
||||
ret |= LCD_WRITE_REG(RM68120_CASET + 2, x1 >> 8);
|
||||
ret |= LCD_WRITE_REG(RM68120_CASET + 3, x1 & 0xff);
|
||||
ret |= LCD_WRITE_REG(RM68120_RASET, y0 >> 8);
|
||||
ret |= LCD_WRITE_REG(RM68120_RASET + 1, y0 & 0xff);
|
||||
ret |= LCD_WRITE_REG(RM68120_RASET + 2, y1 >> 8);
|
||||
ret |= LCD_WRITE_REG(RM68120_RASET + 3, y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(RM68120_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_rm68120_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_rm68120_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_rm68120_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_rm68120_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void lcd_rm68120_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x0100); // Software Reset
|
||||
vTaskDelay(10 / portTICK_RATE_MS);
|
||||
LCD_WRITE_REG(0xF000, 0x55);
|
||||
LCD_WRITE_REG(0xF001, 0xAA);
|
||||
LCD_WRITE_REG(0xF002, 0x52);
|
||||
LCD_WRITE_REG(0xF003, 0x08);
|
||||
LCD_WRITE_REG(0xF004, 0x01);
|
||||
|
||||
//GAMMA SETING RED
|
||||
LCD_WRITE_REG(0xD100, 0x00);
|
||||
LCD_WRITE_REG(0xD101, 0x00);
|
||||
LCD_WRITE_REG(0xD102, 0x1b);
|
||||
LCD_WRITE_REG(0xD103, 0x44);
|
||||
LCD_WRITE_REG(0xD104, 0x62);
|
||||
LCD_WRITE_REG(0xD105, 0x00);
|
||||
LCD_WRITE_REG(0xD106, 0x7b);
|
||||
LCD_WRITE_REG(0xD107, 0xa1);
|
||||
LCD_WRITE_REG(0xD108, 0xc0);
|
||||
LCD_WRITE_REG(0xD109, 0xee);
|
||||
LCD_WRITE_REG(0xD10A, 0x55);
|
||||
LCD_WRITE_REG(0xD10B, 0x10);
|
||||
LCD_WRITE_REG(0xD10C, 0x2c);
|
||||
LCD_WRITE_REG(0xD10D, 0x43);
|
||||
LCD_WRITE_REG(0xD10E, 0x57);
|
||||
LCD_WRITE_REG(0xD10F, 0x55);
|
||||
LCD_WRITE_REG(0xD110, 0x68);
|
||||
LCD_WRITE_REG(0xD111, 0x78);
|
||||
LCD_WRITE_REG(0xD112, 0x87);
|
||||
LCD_WRITE_REG(0xD113, 0x94);
|
||||
LCD_WRITE_REG(0xD114, 0x55);
|
||||
LCD_WRITE_REG(0xD115, 0xa0);
|
||||
LCD_WRITE_REG(0xD116, 0xac);
|
||||
LCD_WRITE_REG(0xD117, 0xb6);
|
||||
LCD_WRITE_REG(0xD118, 0xc1);
|
||||
LCD_WRITE_REG(0xD119, 0x55);
|
||||
LCD_WRITE_REG(0xD11A, 0xcb);
|
||||
LCD_WRITE_REG(0xD11B, 0xcd);
|
||||
LCD_WRITE_REG(0xD11C, 0xd6);
|
||||
LCD_WRITE_REG(0xD11D, 0xdf);
|
||||
LCD_WRITE_REG(0xD11E, 0x95);
|
||||
LCD_WRITE_REG(0xD11F, 0xe8);
|
||||
LCD_WRITE_REG(0xD120, 0xf1);
|
||||
LCD_WRITE_REG(0xD121, 0xfa);
|
||||
LCD_WRITE_REG(0xD122, 0x02);
|
||||
LCD_WRITE_REG(0xD123, 0xaa);
|
||||
LCD_WRITE_REG(0xD124, 0x0b);
|
||||
LCD_WRITE_REG(0xD125, 0x13);
|
||||
LCD_WRITE_REG(0xD126, 0x1d);
|
||||
LCD_WRITE_REG(0xD127, 0x26);
|
||||
LCD_WRITE_REG(0xD128, 0xaa);
|
||||
LCD_WRITE_REG(0xD129, 0x30);
|
||||
LCD_WRITE_REG(0xD12A, 0x3c);
|
||||
LCD_WRITE_REG(0xD12B, 0x4A);
|
||||
LCD_WRITE_REG(0xD12C, 0x63);
|
||||
LCD_WRITE_REG(0xD12D, 0xea);
|
||||
LCD_WRITE_REG(0xD12E, 0x79);
|
||||
LCD_WRITE_REG(0xD12F, 0xa6);
|
||||
LCD_WRITE_REG(0xD130, 0xd0);
|
||||
LCD_WRITE_REG(0xD131, 0x20);
|
||||
LCD_WRITE_REG(0xD132, 0x0f);
|
||||
LCD_WRITE_REG(0xD133, 0x8e);
|
||||
LCD_WRITE_REG(0xD134, 0xff);
|
||||
//GAMMA SETING GREEN
|
||||
LCD_WRITE_REG(0xD200, 0x00);
|
||||
LCD_WRITE_REG(0xD201, 0x00);
|
||||
LCD_WRITE_REG(0xD202, 0x1b);
|
||||
LCD_WRITE_REG(0xD203, 0x44);
|
||||
LCD_WRITE_REG(0xD204, 0x62);
|
||||
LCD_WRITE_REG(0xD205, 0x00);
|
||||
LCD_WRITE_REG(0xD206, 0x7b);
|
||||
LCD_WRITE_REG(0xD207, 0xa1);
|
||||
LCD_WRITE_REG(0xD208, 0xc0);
|
||||
LCD_WRITE_REG(0xD209, 0xee);
|
||||
LCD_WRITE_REG(0xD20A, 0x55);
|
||||
LCD_WRITE_REG(0xD20B, 0x10);
|
||||
LCD_WRITE_REG(0xD20C, 0x2c);
|
||||
LCD_WRITE_REG(0xD20D, 0x43);
|
||||
LCD_WRITE_REG(0xD20E, 0x57);
|
||||
LCD_WRITE_REG(0xD20F, 0x55);
|
||||
LCD_WRITE_REG(0xD210, 0x68);
|
||||
LCD_WRITE_REG(0xD211, 0x78);
|
||||
LCD_WRITE_REG(0xD212, 0x87);
|
||||
LCD_WRITE_REG(0xD213, 0x94);
|
||||
LCD_WRITE_REG(0xD214, 0x55);
|
||||
LCD_WRITE_REG(0xD215, 0xa0);
|
||||
LCD_WRITE_REG(0xD216, 0xac);
|
||||
LCD_WRITE_REG(0xD217, 0xb6);
|
||||
LCD_WRITE_REG(0xD218, 0xc1);
|
||||
LCD_WRITE_REG(0xD219, 0x55);
|
||||
LCD_WRITE_REG(0xD21A, 0xcb);
|
||||
LCD_WRITE_REG(0xD21B, 0xcd);
|
||||
LCD_WRITE_REG(0xD21C, 0xd6);
|
||||
LCD_WRITE_REG(0xD21D, 0xdf);
|
||||
LCD_WRITE_REG(0xD21E, 0x95);
|
||||
LCD_WRITE_REG(0xD21F, 0xe8);
|
||||
LCD_WRITE_REG(0xD220, 0xf1);
|
||||
LCD_WRITE_REG(0xD221, 0xfa);
|
||||
LCD_WRITE_REG(0xD222, 0x02);
|
||||
LCD_WRITE_REG(0xD223, 0xaa);
|
||||
LCD_WRITE_REG(0xD224, 0x0b);
|
||||
LCD_WRITE_REG(0xD225, 0x13);
|
||||
LCD_WRITE_REG(0xD226, 0x1d);
|
||||
LCD_WRITE_REG(0xD227, 0x26);
|
||||
LCD_WRITE_REG(0xD228, 0xaa);
|
||||
LCD_WRITE_REG(0xD229, 0x30);
|
||||
LCD_WRITE_REG(0xD22A, 0x3c);
|
||||
LCD_WRITE_REG(0xD22B, 0x4a);
|
||||
LCD_WRITE_REG(0xD22C, 0x63);
|
||||
LCD_WRITE_REG(0xD22D, 0xea);
|
||||
LCD_WRITE_REG(0xD22E, 0x79);
|
||||
LCD_WRITE_REG(0xD22F, 0xa6);
|
||||
LCD_WRITE_REG(0xD230, 0xd0);
|
||||
LCD_WRITE_REG(0xD231, 0x20);
|
||||
LCD_WRITE_REG(0xD232, 0x0f);
|
||||
LCD_WRITE_REG(0xD233, 0x8e);
|
||||
LCD_WRITE_REG(0xD234, 0xff);
|
||||
|
||||
//GAMMA SETING BLUE
|
||||
LCD_WRITE_REG(0xD300, 0x00);
|
||||
LCD_WRITE_REG(0xD301, 0x00);
|
||||
LCD_WRITE_REG(0xD302, 0x1b);
|
||||
LCD_WRITE_REG(0xD303, 0x44);
|
||||
LCD_WRITE_REG(0xD304, 0x62);
|
||||
LCD_WRITE_REG(0xD305, 0x00);
|
||||
LCD_WRITE_REG(0xD306, 0x7b);
|
||||
LCD_WRITE_REG(0xD307, 0xa1);
|
||||
LCD_WRITE_REG(0xD308, 0xc0);
|
||||
LCD_WRITE_REG(0xD309, 0xee);
|
||||
LCD_WRITE_REG(0xD30A, 0x55);
|
||||
LCD_WRITE_REG(0xD30B, 0x10);
|
||||
LCD_WRITE_REG(0xD30C, 0x2c);
|
||||
LCD_WRITE_REG(0xD30D, 0x43);
|
||||
LCD_WRITE_REG(0xD30E, 0x57);
|
||||
LCD_WRITE_REG(0xD30F, 0x55);
|
||||
LCD_WRITE_REG(0xD310, 0x68);
|
||||
LCD_WRITE_REG(0xD311, 0x78);
|
||||
LCD_WRITE_REG(0xD312, 0x87);
|
||||
LCD_WRITE_REG(0xD313, 0x94);
|
||||
LCD_WRITE_REG(0xD314, 0x55);
|
||||
LCD_WRITE_REG(0xD315, 0xa0);
|
||||
LCD_WRITE_REG(0xD316, 0xac);
|
||||
LCD_WRITE_REG(0xD317, 0xb6);
|
||||
LCD_WRITE_REG(0xD318, 0xc1);
|
||||
LCD_WRITE_REG(0xD319, 0x55);
|
||||
LCD_WRITE_REG(0xD31A, 0xcb);
|
||||
LCD_WRITE_REG(0xD31B, 0xcd);
|
||||
LCD_WRITE_REG(0xD31C, 0xd6);
|
||||
LCD_WRITE_REG(0xD31D, 0xdf);
|
||||
LCD_WRITE_REG(0xD31E, 0x95);
|
||||
LCD_WRITE_REG(0xD31F, 0xe8);
|
||||
LCD_WRITE_REG(0xD320, 0xf1);
|
||||
LCD_WRITE_REG(0xD321, 0xfa);
|
||||
LCD_WRITE_REG(0xD322, 0x02);
|
||||
LCD_WRITE_REG(0xD323, 0xaa);
|
||||
LCD_WRITE_REG(0xD324, 0x0b);
|
||||
LCD_WRITE_REG(0xD325, 0x13);
|
||||
LCD_WRITE_REG(0xD326, 0x1d);
|
||||
LCD_WRITE_REG(0xD327, 0x26);
|
||||
LCD_WRITE_REG(0xD328, 0xaa);
|
||||
LCD_WRITE_REG(0xD329, 0x30);
|
||||
LCD_WRITE_REG(0xD32A, 0x3c);
|
||||
LCD_WRITE_REG(0xD32B, 0x4A);
|
||||
LCD_WRITE_REG(0xD32C, 0x63);
|
||||
LCD_WRITE_REG(0xD32D, 0xea);
|
||||
LCD_WRITE_REG(0xD32E, 0x79);
|
||||
LCD_WRITE_REG(0xD32F, 0xa6);
|
||||
LCD_WRITE_REG(0xD330, 0xd0);
|
||||
LCD_WRITE_REG(0xD331, 0x20);
|
||||
LCD_WRITE_REG(0xD332, 0x0f);
|
||||
LCD_WRITE_REG(0xD333, 0x8e);
|
||||
LCD_WRITE_REG(0xD334, 0xff);
|
||||
|
||||
|
||||
//GAMMA SETING RED
|
||||
LCD_WRITE_REG(0xD400, 0x00);
|
||||
LCD_WRITE_REG(0xD401, 0x00);
|
||||
LCD_WRITE_REG(0xD402, 0x1b);
|
||||
LCD_WRITE_REG(0xD403, 0x44);
|
||||
LCD_WRITE_REG(0xD404, 0x62);
|
||||
LCD_WRITE_REG(0xD405, 0x00);
|
||||
LCD_WRITE_REG(0xD406, 0x7b);
|
||||
LCD_WRITE_REG(0xD407, 0xa1);
|
||||
LCD_WRITE_REG(0xD408, 0xc0);
|
||||
LCD_WRITE_REG(0xD409, 0xee);
|
||||
LCD_WRITE_REG(0xD40A, 0x55);
|
||||
LCD_WRITE_REG(0xD40B, 0x10);
|
||||
LCD_WRITE_REG(0xD40C, 0x2c);
|
||||
LCD_WRITE_REG(0xD40D, 0x43);
|
||||
LCD_WRITE_REG(0xD40E, 0x57);
|
||||
LCD_WRITE_REG(0xD40F, 0x55);
|
||||
LCD_WRITE_REG(0xD410, 0x68);
|
||||
LCD_WRITE_REG(0xD411, 0x78);
|
||||
LCD_WRITE_REG(0xD412, 0x87);
|
||||
LCD_WRITE_REG(0xD413, 0x94);
|
||||
LCD_WRITE_REG(0xD414, 0x55);
|
||||
LCD_WRITE_REG(0xD415, 0xa0);
|
||||
LCD_WRITE_REG(0xD416, 0xac);
|
||||
LCD_WRITE_REG(0xD417, 0xb6);
|
||||
LCD_WRITE_REG(0xD418, 0xc1);
|
||||
LCD_WRITE_REG(0xD419, 0x55);
|
||||
LCD_WRITE_REG(0xD41A, 0xcb);
|
||||
LCD_WRITE_REG(0xD41B, 0xcd);
|
||||
LCD_WRITE_REG(0xD41C, 0xd6);
|
||||
LCD_WRITE_REG(0xD41D, 0xdf);
|
||||
LCD_WRITE_REG(0xD41E, 0x95);
|
||||
LCD_WRITE_REG(0xD41F, 0xe8);
|
||||
LCD_WRITE_REG(0xD420, 0xf1);
|
||||
LCD_WRITE_REG(0xD421, 0xfa);
|
||||
LCD_WRITE_REG(0xD422, 0x02);
|
||||
LCD_WRITE_REG(0xD423, 0xaa);
|
||||
LCD_WRITE_REG(0xD424, 0x0b);
|
||||
LCD_WRITE_REG(0xD425, 0x13);
|
||||
LCD_WRITE_REG(0xD426, 0x1d);
|
||||
LCD_WRITE_REG(0xD427, 0x26);
|
||||
LCD_WRITE_REG(0xD428, 0xaa);
|
||||
LCD_WRITE_REG(0xD429, 0x30);
|
||||
LCD_WRITE_REG(0xD42A, 0x3c);
|
||||
LCD_WRITE_REG(0xD42B, 0x4A);
|
||||
LCD_WRITE_REG(0xD42C, 0x63);
|
||||
LCD_WRITE_REG(0xD42D, 0xea);
|
||||
LCD_WRITE_REG(0xD42E, 0x79);
|
||||
LCD_WRITE_REG(0xD42F, 0xa6);
|
||||
LCD_WRITE_REG(0xD430, 0xd0);
|
||||
LCD_WRITE_REG(0xD431, 0x20);
|
||||
LCD_WRITE_REG(0xD432, 0x0f);
|
||||
LCD_WRITE_REG(0xD433, 0x8e);
|
||||
LCD_WRITE_REG(0xD434, 0xff);
|
||||
|
||||
//GAMMA SETING GREEN
|
||||
LCD_WRITE_REG(0xD500, 0x00);
|
||||
LCD_WRITE_REG(0xD501, 0x00);
|
||||
LCD_WRITE_REG(0xD502, 0x1b);
|
||||
LCD_WRITE_REG(0xD503, 0x44);
|
||||
LCD_WRITE_REG(0xD504, 0x62);
|
||||
LCD_WRITE_REG(0xD505, 0x00);
|
||||
LCD_WRITE_REG(0xD506, 0x7b);
|
||||
LCD_WRITE_REG(0xD507, 0xa1);
|
||||
LCD_WRITE_REG(0xD508, 0xc0);
|
||||
LCD_WRITE_REG(0xD509, 0xee);
|
||||
LCD_WRITE_REG(0xD50A, 0x55);
|
||||
LCD_WRITE_REG(0xD50B, 0x10);
|
||||
LCD_WRITE_REG(0xD50C, 0x2c);
|
||||
LCD_WRITE_REG(0xD50D, 0x43);
|
||||
LCD_WRITE_REG(0xD50E, 0x57);
|
||||
LCD_WRITE_REG(0xD50F, 0x55);
|
||||
LCD_WRITE_REG(0xD510, 0x68);
|
||||
LCD_WRITE_REG(0xD511, 0x78);
|
||||
LCD_WRITE_REG(0xD512, 0x87);
|
||||
LCD_WRITE_REG(0xD513, 0x94);
|
||||
LCD_WRITE_REG(0xD514, 0x55);
|
||||
LCD_WRITE_REG(0xD515, 0xa0);
|
||||
LCD_WRITE_REG(0xD516, 0xac);
|
||||
LCD_WRITE_REG(0xD517, 0xb6);
|
||||
LCD_WRITE_REG(0xD518, 0xc1);
|
||||
LCD_WRITE_REG(0xD519, 0x55);
|
||||
LCD_WRITE_REG(0xD51A, 0xcb);
|
||||
LCD_WRITE_REG(0xD51B, 0xcd);
|
||||
LCD_WRITE_REG(0xD51C, 0xd6);
|
||||
LCD_WRITE_REG(0xD51D, 0xdf);
|
||||
LCD_WRITE_REG(0xD51E, 0x95);
|
||||
LCD_WRITE_REG(0xD51F, 0xe8);
|
||||
LCD_WRITE_REG(0xD520, 0xf1);
|
||||
LCD_WRITE_REG(0xD521, 0xfa);
|
||||
LCD_WRITE_REG(0xD522, 0x02);
|
||||
LCD_WRITE_REG(0xD523, 0xaa);
|
||||
LCD_WRITE_REG(0xD524, 0x0b);
|
||||
LCD_WRITE_REG(0xD525, 0x13);
|
||||
LCD_WRITE_REG(0xD526, 0x1d);
|
||||
LCD_WRITE_REG(0xD527, 0x26);
|
||||
LCD_WRITE_REG(0xD528, 0xaa);
|
||||
LCD_WRITE_REG(0xD529, 0x30);
|
||||
LCD_WRITE_REG(0xD52A, 0x3c);
|
||||
LCD_WRITE_REG(0xD52B, 0x4a);
|
||||
LCD_WRITE_REG(0xD52C, 0x63);
|
||||
LCD_WRITE_REG(0xD52D, 0xea);
|
||||
LCD_WRITE_REG(0xD52E, 0x79);
|
||||
LCD_WRITE_REG(0xD52F, 0xa6);
|
||||
LCD_WRITE_REG(0xD530, 0xd0);
|
||||
LCD_WRITE_REG(0xD531, 0x20);
|
||||
LCD_WRITE_REG(0xD532, 0x0f);
|
||||
LCD_WRITE_REG(0xD533, 0x8e);
|
||||
LCD_WRITE_REG(0xD534, 0xff);
|
||||
|
||||
//GAMMA SETING BLUE
|
||||
LCD_WRITE_REG(0xD600, 0x00);
|
||||
LCD_WRITE_REG(0xD601, 0x00);
|
||||
LCD_WRITE_REG(0xD602, 0x1b);
|
||||
LCD_WRITE_REG(0xD603, 0x44);
|
||||
LCD_WRITE_REG(0xD604, 0x62);
|
||||
LCD_WRITE_REG(0xD605, 0x00);
|
||||
LCD_WRITE_REG(0xD606, 0x7b);
|
||||
LCD_WRITE_REG(0xD607, 0xa1);
|
||||
LCD_WRITE_REG(0xD608, 0xc0);
|
||||
LCD_WRITE_REG(0xD609, 0xee);
|
||||
LCD_WRITE_REG(0xD60A, 0x55);
|
||||
LCD_WRITE_REG(0xD60B, 0x10);
|
||||
LCD_WRITE_REG(0xD60C, 0x2c);
|
||||
LCD_WRITE_REG(0xD60D, 0x43);
|
||||
LCD_WRITE_REG(0xD60E, 0x57);
|
||||
LCD_WRITE_REG(0xD60F, 0x55);
|
||||
LCD_WRITE_REG(0xD610, 0x68);
|
||||
LCD_WRITE_REG(0xD611, 0x78);
|
||||
LCD_WRITE_REG(0xD612, 0x87);
|
||||
LCD_WRITE_REG(0xD613, 0x94);
|
||||
LCD_WRITE_REG(0xD614, 0x55);
|
||||
LCD_WRITE_REG(0xD615, 0xa0);
|
||||
LCD_WRITE_REG(0xD616, 0xac);
|
||||
LCD_WRITE_REG(0xD617, 0xb6);
|
||||
LCD_WRITE_REG(0xD618, 0xc1);
|
||||
LCD_WRITE_REG(0xD619, 0x55);
|
||||
LCD_WRITE_REG(0xD61A, 0xcb);
|
||||
LCD_WRITE_REG(0xD61B, 0xcd);
|
||||
LCD_WRITE_REG(0xD61C, 0xd6);
|
||||
LCD_WRITE_REG(0xD61D, 0xdf);
|
||||
LCD_WRITE_REG(0xD61E, 0x95);
|
||||
LCD_WRITE_REG(0xD61F, 0xe8);
|
||||
LCD_WRITE_REG(0xD620, 0xf1);
|
||||
LCD_WRITE_REG(0xD621, 0xfa);
|
||||
LCD_WRITE_REG(0xD622, 0x02);
|
||||
LCD_WRITE_REG(0xD623, 0xaa);
|
||||
LCD_WRITE_REG(0xD624, 0x0b);
|
||||
LCD_WRITE_REG(0xD625, 0x13);
|
||||
LCD_WRITE_REG(0xD626, 0x1d);
|
||||
LCD_WRITE_REG(0xD627, 0x26);
|
||||
LCD_WRITE_REG(0xD628, 0xaa);
|
||||
LCD_WRITE_REG(0xD629, 0x30);
|
||||
LCD_WRITE_REG(0xD62A, 0x3c);
|
||||
LCD_WRITE_REG(0xD62B, 0x4A);
|
||||
LCD_WRITE_REG(0xD62C, 0x63);
|
||||
LCD_WRITE_REG(0xD62D, 0xea);
|
||||
LCD_WRITE_REG(0xD62E, 0x79);
|
||||
LCD_WRITE_REG(0xD62F, 0xa6);
|
||||
LCD_WRITE_REG(0xD630, 0xd0);
|
||||
LCD_WRITE_REG(0xD631, 0x20);
|
||||
LCD_WRITE_REG(0xD632, 0x0f);
|
||||
LCD_WRITE_REG(0xD633, 0x8e);
|
||||
LCD_WRITE_REG(0xD634, 0xff);
|
||||
|
||||
//AVDD VOLTAGE SETTING
|
||||
LCD_WRITE_REG(0xB000, 0x05);
|
||||
LCD_WRITE_REG(0xB001, 0x05);
|
||||
LCD_WRITE_REG(0xB002, 0x05);
|
||||
//AVEE VOLTAGE SETTING
|
||||
LCD_WRITE_REG(0xB100, 0x05);
|
||||
LCD_WRITE_REG(0xB101, 0x05);
|
||||
LCD_WRITE_REG(0xB102, 0x05);
|
||||
|
||||
//AVDD Boosting
|
||||
LCD_WRITE_REG(0xB600, 0x34);
|
||||
LCD_WRITE_REG(0xB601, 0x34);
|
||||
LCD_WRITE_REG(0xB603, 0x34);
|
||||
//AVEE Boosting
|
||||
LCD_WRITE_REG(0xB700, 0x24);
|
||||
LCD_WRITE_REG(0xB701, 0x24);
|
||||
LCD_WRITE_REG(0xB702, 0x24);
|
||||
//VCL Boosting
|
||||
LCD_WRITE_REG(0xB800, 0x24);
|
||||
LCD_WRITE_REG(0xB801, 0x24);
|
||||
LCD_WRITE_REG(0xB802, 0x24);
|
||||
//VGLX VOLTAGE SETTING
|
||||
LCD_WRITE_REG(0xBA00, 0x14);
|
||||
LCD_WRITE_REG(0xBA01, 0x14);
|
||||
LCD_WRITE_REG(0xBA02, 0x14);
|
||||
//VCL Boosting
|
||||
LCD_WRITE_REG(0xB900, 0x24);
|
||||
LCD_WRITE_REG(0xB901, 0x24);
|
||||
LCD_WRITE_REG(0xB902, 0x24);
|
||||
//Gamma Voltage
|
||||
LCD_WRITE_REG(0xBc00, 0x00);
|
||||
LCD_WRITE_REG(0xBc01, 0xa0);//vgmp=5.0
|
||||
LCD_WRITE_REG(0xBc02, 0x00);
|
||||
LCD_WRITE_REG(0xBd00, 0x00);
|
||||
LCD_WRITE_REG(0xBd01, 0xa0);//vgmn=5.0
|
||||
LCD_WRITE_REG(0xBd02, 0x00);
|
||||
//VCOM Setting
|
||||
LCD_WRITE_REG(0xBe01, 0x3d);//3
|
||||
//ENABLE PAGE 0
|
||||
LCD_WRITE_REG(0xF000, 0x55);
|
||||
LCD_WRITE_REG(0xF001, 0xAA);
|
||||
LCD_WRITE_REG(0xF002, 0x52);
|
||||
LCD_WRITE_REG(0xF003, 0x08);
|
||||
LCD_WRITE_REG(0xF004, 0x00);
|
||||
//Vivid Color Function Control
|
||||
LCD_WRITE_REG(0xB400, 0x10);
|
||||
//Z-INVERSION
|
||||
LCD_WRITE_REG(0xBC00, 0x05);
|
||||
LCD_WRITE_REG(0xBC01, 0x05);
|
||||
LCD_WRITE_REG(0xBC02, 0x05);
|
||||
|
||||
//*************** add on 20111021**********************//
|
||||
LCD_WRITE_REG(0xB700, 0x22);//GATE EQ CONTROL
|
||||
LCD_WRITE_REG(0xB701, 0x22);//GATE EQ CONTROL
|
||||
LCD_WRITE_REG(0xC80B, 0x2A);//DISPLAY TIMING CONTROL
|
||||
LCD_WRITE_REG(0xC80C, 0x2A);//DISPLAY TIMING CONTROL
|
||||
LCD_WRITE_REG(0xC80F, 0x2A);//DISPLAY TIMING CONTROL
|
||||
LCD_WRITE_REG(0xC810, 0x2A);//DISPLAY TIMING CONTROL
|
||||
//*************** add on 20111021**********************//
|
||||
//PWM_ENH_OE =1
|
||||
LCD_WRITE_REG(0xd000, 0x01);
|
||||
//DM_SEL =1
|
||||
LCD_WRITE_REG(0xb300, 0x10);
|
||||
//VBPDA=07h
|
||||
LCD_WRITE_REG(0xBd02, 0x07);
|
||||
//VBPDb=07h
|
||||
LCD_WRITE_REG(0xBe02, 0x07);
|
||||
//VBPDc=07h
|
||||
LCD_WRITE_REG(0xBf02, 0x07);
|
||||
//ENABLE PAGE 2
|
||||
LCD_WRITE_REG(0xF000, 0x55);
|
||||
LCD_WRITE_REG(0xF001, 0xAA);
|
||||
LCD_WRITE_REG(0xF002, 0x52);
|
||||
LCD_WRITE_REG(0xF003, 0x08);
|
||||
LCD_WRITE_REG(0xF004, 0x02);
|
||||
//SDREG0 =0
|
||||
LCD_WRITE_REG(0xc301, 0xa9);
|
||||
//DS=14
|
||||
LCD_WRITE_REG(0xfe01, 0x94);
|
||||
//OSC =60h
|
||||
LCD_WRITE_REG(0xf600, 0x60);
|
||||
//TE ON
|
||||
LCD_WRITE_REG(0x3500, 0x00);
|
||||
//SLEEP OUT
|
||||
LCD_WRITE_CMD(0x1100);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
//DISPLY ON
|
||||
LCD_WRITE_CMD(0x2900);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
|
||||
LCD_WRITE_REG(0x3A00, 0x55);
|
||||
LCD_WRITE_REG(0x3600, 0xA3);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
#ifndef _IOT_RM68120_H_
|
||||
#define _IOT_RM68120_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_rm68120_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_rm68120_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _IOT_RM68120_H_ */
|
||||
@@ -0,0 +1,364 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ssd1306.h"
|
||||
|
||||
static const char *TAG = "lcd ssd1306";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "OLED SSD1306"
|
||||
#define LCD_BPP 1
|
||||
|
||||
// Some fundamental define for screen controller
|
||||
#define SSD1306_PAGES 8
|
||||
#define SSD1306_HEIGHT 64
|
||||
#define SSD1306_COLUMNS 128
|
||||
|
||||
// Control byte
|
||||
#define SSD1306_CONTROL_BYTE_CMD_SINGLE 0x80
|
||||
#define SSD1306_CONTROL_BYTE_CMD_STREAM 0x00
|
||||
#define SSD1306_CONTROL_BYTE_DATA_STREAM 0x40
|
||||
|
||||
// Fundamental commands (pg.28)
|
||||
#define SSD1306_CMD_SET_CONTRAST 0x81 // follow with 0x7F
|
||||
#define SSD1306_CMD_DISPLAY_RAM 0xA4
|
||||
#define SSD1306_CMD_DISPLAY_ALLON 0xA5
|
||||
#define SSD1306_CMD_DISPLAY_NORMAL 0xA6
|
||||
#define SSD1306_CMD_DISPLAY_INVERTED 0xA7
|
||||
#define SSD1306_CMD_DISPLAY_OFF 0xAE
|
||||
#define SSD1306_CMD_DISPLAY_ON 0xAF
|
||||
|
||||
// Display Scrolling Parameters
|
||||
#define SSD1306_CMD_RIGHT_HORIZONTAL_SCROLL 0x26 // Init rt scroll
|
||||
#define SSD1306_CMD_LEFT_HORIZONTAL_SCROLL 0x27 // Init left scroll
|
||||
#define SSD1306_CMD_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 // Init diag scroll
|
||||
#define SSD1306_CMD_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A // Init diag scroll
|
||||
#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E // Stop scroll
|
||||
#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F // Start scroll
|
||||
#define SSD1306_CMD_SET_VERTICAL_SCROLL_AREA 0xA3 // Set scroll range
|
||||
|
||||
// Addressing Command Table (pg.30)
|
||||
#define SSD1306_CMD_SET_LOWER_COLUMN_ADDR 0x00 // Set Lower Column Start Address for Page Addressing Mode, using X[3:0]
|
||||
#define SSD1306_CMD_SET_HIGHER_COLUMN_ADDR 0x10 // Set Higher Column Start Address for Page Addressing Mode, using X[3:0]
|
||||
#define SSD1306_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 00b= Horizontal Addressing Mode; 01b=Vertical Addressing Mode; 10b= Page Addressing Mode (RESET)
|
||||
#define SSD1306_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127
|
||||
#define SSD1306_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7
|
||||
#define SSD1306_CMD_SET_PAGE_ADDR 0xB0
|
||||
|
||||
// Hardware Config (pg.31)
|
||||
#define SSD1306_CMD_SET_DISPLAY_START_LINE 0x40
|
||||
#define SSD1306_CMD_SET_SEGMENT_REMAP 0xA1
|
||||
#define SSD1306_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX
|
||||
#define SSD1306_CMD_SET_COM_SCAN_MODE_NORMAL 0xC0
|
||||
#define SSD1306_CMD_SET_COM_SCAN_MODE_REMAP 0xC8
|
||||
#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
|
||||
#define SSD1306_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12
|
||||
#define SSD1306_CMD_NOP 0xE3 // NOP
|
||||
|
||||
// Timing and Driving Scheme (pg.32)
|
||||
#define SSD1306_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
|
||||
#define SSD1306_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1
|
||||
#define SSD1306_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30
|
||||
|
||||
// Charge Pump (pg.62)
|
||||
#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14
|
||||
|
||||
#define SSD1306_COLUMN_ADDR 0x00
|
||||
#define SSD1306_LOWER_ADDRESS (SSD1306_CMD_SET_LOWER_COLUMN_ADDR + (SSD1306_COLUMN_ADDR&0x0f))
|
||||
#define SSD1306_HIGHER_ADDRESS (SSD1306_CMD_SET_HIGHER_COLUMN_ADDR + ((SSD1306_COLUMN_ADDR&0xf0)>>4))
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
static esp_err_t lcd_ssd1306_write_ram_data(uint16_t color);
|
||||
|
||||
scr_driver_t lcd_ssd1306_default_driver = {
|
||||
.init = lcd_ssd1306_init,
|
||||
.deinit = lcd_ssd1306_deinit,
|
||||
.set_direction = lcd_ssd1306_set_rotate,
|
||||
.set_window = lcd_ssd1306_set_window,
|
||||
.write_ram_data = lcd_ssd1306_write_ram_data,
|
||||
.draw_pixel = lcd_ssd1306_draw_pixel,
|
||||
.draw_bitmap = lcd_ssd1306_draw_bitmap,
|
||||
.get_info = lcd_ssd1306_get_info,
|
||||
};
|
||||
|
||||
esp_err_t lcd_ssd1306_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= SSD1306_COLUMNS, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= SSD1306_HEIGHT, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
LCD_WRITE_CMD(0xAE); //--turn off oled panel
|
||||
LCD_WRITE_CMD(0x00); //---set low column address
|
||||
LCD_WRITE_CMD(0x10); //---set high column address
|
||||
LCD_WRITE_CMD(0x40); //--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
|
||||
LCD_WRITE_CMD(0x81); //--set contrast control register
|
||||
LCD_WRITE_CMD(0xCF); // Set SEG Output Current Brightness
|
||||
LCD_WRITE_CMD(0xA1); //--Set SEG/Column Mapping
|
||||
LCD_WRITE_CMD(0xC0); //Set COM/Row Scan Direction
|
||||
LCD_WRITE_CMD(0xA6); //--set normal display
|
||||
LCD_WRITE_CMD(0xA8); //--set multiplex ratio(1 to 64)
|
||||
LCD_WRITE_CMD(0x3f); //--1/64 duty
|
||||
LCD_WRITE_CMD(0xD3); //-set display offset Shift Mapping RAM Counter (0x00~0x3F)
|
||||
LCD_WRITE_CMD(0x00); //-not offset
|
||||
LCD_WRITE_CMD(0xd5); //--set display clock divide ratio/oscillator frequency
|
||||
LCD_WRITE_CMD(0x80); //--set divide ratio, Set Clock as 100 Frames/Sec
|
||||
LCD_WRITE_CMD(0xD9); //--set pre-charge period
|
||||
LCD_WRITE_CMD(0xF1); //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
|
||||
LCD_WRITE_CMD(0xDA); //--set com pins hardware configuration
|
||||
LCD_WRITE_CMD(0x12);
|
||||
LCD_WRITE_CMD(0xDB); //--set vcomh
|
||||
LCD_WRITE_CMD(0x40); //Set VCOM Deselect Level
|
||||
LCD_WRITE_CMD(0x20); //-Set Page Addressing Mode (0x00/0x01/0x02)
|
||||
LCD_WRITE_CMD(0x02);
|
||||
LCD_WRITE_CMD(0x8D); //--set Charge Pump enable/disable
|
||||
LCD_WRITE_CMD(0x14); //--set(0x10) disable
|
||||
LCD_WRITE_CMD(0xA4); // Disable Entire Display On (0xa4/0xa5)
|
||||
LCD_WRITE_CMD(0xA6); // Disable Inverse Display On (0xa6/a7)
|
||||
LCD_WRITE_CMD(0xAF); //--turn on oled panel
|
||||
|
||||
lcd_ssd1306_set_rotate(lcd_conf->rotate);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_MONO;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_set_rotate(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
ret |= LCD_WRITE_CMD(0xA0);
|
||||
ret |= LCD_WRITE_CMD(0xC0);
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
ret |= LCD_WRITE_CMD(0xA0);
|
||||
ret |= LCD_WRITE_CMD(0xC8);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
ret |= LCD_WRITE_CMD(0xA1);
|
||||
ret |= LCD_WRITE_CMD(0xC0);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
ret |= LCD_WRITE_CMD(0xA1);
|
||||
ret |= LCD_WRITE_CMD(0xC8);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupport rotate direction");
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
y1 += 1;
|
||||
LCD_CHECK((0 == (y0 % 8)), "y0 should be a multiple of 8", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((0 == (y1 % 8)), "(y1+1) should be a multiple of 8", ESP_ERR_INVALID_ARG);
|
||||
uint8_t row1 = y0 >> 3;
|
||||
uint8_t row2 = y1 / 8;
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_SET_MEMORY_ADDR_MODE);
|
||||
ret |= LCD_WRITE_CMD(0); /**< Set to Horizontal Addressing Mode */
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_SET_COLUMN_RANGE);
|
||||
ret |= LCD_WRITE_CMD(x0);
|
||||
ret |= LCD_WRITE_CMD(x1 - 1);
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_SET_PAGE_RANGE);
|
||||
ret |= LCD_WRITE_CMD(row1);
|
||||
ret |= LCD_WRITE_CMD(row2 - 1);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_ssd1306_write_ram_data(uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "lcd ssd1306 unsupport write ram data");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "SSD1306 not support draw pixel without buffer");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
LCD_CHECK((x + w <= g_lcd_handle.width) && (y + h <= g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t *p = (uint8_t *)bitmap;
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ssd1306_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = LCD_WRITE(p, w * LCD_BPP / 8 * h);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "Draw bitmap failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_display_on(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(OLED_CMD_SET_CHARGE_PUMP); // SET DCDC
|
||||
ret |= LCD_WRITE_CMD(0X14); // Enable charge pump during display on
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_DISPLAY_ON); // DISPLAY ON
|
||||
LCD_CHECK(ESP_OK == ret, "Set display ON failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_display_off(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(OLED_CMD_SET_CHARGE_PUMP); //SET DCDC
|
||||
ret |= LCD_WRITE_CMD(0X10); // Disable charge pump
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_DISPLAY_OFF); //DISPLAY OFF
|
||||
LCD_CHECK(ESP_OK == ret, "Set display OFF failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_set_contrast(uint8_t contrast)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(0x81);
|
||||
ret |= LCD_WRITE_CMD(contrast);
|
||||
LCD_CHECK(ESP_OK == ret, "Set contrast failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_start_horizontal_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval)
|
||||
{
|
||||
LCD_CHECK(start < SSD1306_PAGES, "Start page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(stop < SSD1306_PAGES, "End page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(interval < 8, "Time interval invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
uint8_t cmd = dir ? SSD1306_CMD_LEFT_HORIZONTAL_SCROLL : SSD1306_CMD_RIGHT_HORIZONTAL_SCROLL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= LCD_WRITE_CMD(cmd);
|
||||
ret |= LCD_WRITE_CMD(0x00);
|
||||
ret |= LCD_WRITE_CMD(start);
|
||||
ret |= LCD_WRITE_CMD(interval);
|
||||
ret |= LCD_WRITE_CMD(stop);
|
||||
|
||||
ret |= LCD_WRITE_CMD(0x00);
|
||||
ret |= LCD_WRITE_CMD(0xff);
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_ACTIVATE_SCROLL);
|
||||
LCD_CHECK(ESP_OK == ret, "Start horizontal scroll failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_start_vertical_scroll(uint8_t start, uint8_t stop)
|
||||
{
|
||||
LCD_CHECK(start < SSD1306_PAGES, "Start page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(stop < SSD1306_PAGES, "End page address invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_SET_VERTICAL_SCROLL_AREA);
|
||||
ret |= LCD_WRITE_CMD(start);
|
||||
ret |= LCD_WRITE_CMD(stop);
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_ACTIVATE_SCROLL);
|
||||
LCD_CHECK(ESP_OK == ret, "Start vertical scroll failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_start_scroll_diagRight(uint8_t start, uint8_t stop)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_SET_VERTICAL_SCROLL_AREA);
|
||||
ret |= LCD_WRITE_CMD(0x00);
|
||||
ret |= LCD_WRITE_CMD(32);
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
|
||||
ret |= LCD_WRITE_CMD(0x00);
|
||||
ret |= LCD_WRITE_CMD(start);
|
||||
ret |= LCD_WRITE_CMD(0x00);
|
||||
ret |= LCD_WRITE_CMD(stop);
|
||||
ret |= LCD_WRITE_CMD(0x01);
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_ACTIVATE_SCROLL);
|
||||
LCD_CHECK(ESP_OK == ret, "Start diagRight scroll failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1306_stop_scroll(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret |= LCD_WRITE_CMD(SSD1306_CMD_DEACTIVATE_SCROLL);
|
||||
LCD_CHECK(ESP_OK == ret, "Stop scroll failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
#ifndef __IOT_LCD_SSD1306_H__
|
||||
#define __IOT_LCD_SSD1306_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
/**
|
||||
* @brief device initialization
|
||||
*
|
||||
* @param lcd_conf configuration struct of ssd1306
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitial screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_set_rotate(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
/**
|
||||
* @brief Set the contrast of screen
|
||||
*
|
||||
* @param contrast Contrast to set
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_set_contrast(uint8_t contrast);
|
||||
|
||||
/**
|
||||
* @brief Turn on the screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_display_on(void);
|
||||
|
||||
/**
|
||||
* @brief Turn off the screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_display_off(void);
|
||||
|
||||
/**
|
||||
* @brief Start horizontal scroll
|
||||
*
|
||||
* @param dir Direction of horizontal scroll
|
||||
* @param start start page
|
||||
* @param stop end page
|
||||
* @param interval time interval of scroll
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_start_horizontal_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_start_vertical_scroll(uint8_t start, uint8_t stop);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_start_scroll_diagRight(uint8_t start, uint8_t stop);
|
||||
|
||||
/**
|
||||
* @brief Stop screen scroll
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1306_stop_scroll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,358 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ssd1307.h"
|
||||
|
||||
static const char *TAG = "ssd1307";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "OLED SSD1307"
|
||||
#define LCD_BPP 1
|
||||
|
||||
// Some fundamental define for screen controller
|
||||
#define SSD1307_PAGES 5
|
||||
#define SSD1307_HEIGHT 40
|
||||
#define SSD1307_COLUMNS 128
|
||||
|
||||
// Control byte
|
||||
#define SSD1307_CONTROL_BYTE_CMD_SINGLE 0x80
|
||||
#define SSD1307_CONTROL_BYTE_CMD_STREAM 0x00
|
||||
#define SSD1307_CONTROL_BYTE_DATA_STREAM 0x40
|
||||
|
||||
// Fundamental commands (pg.28)
|
||||
#define SSD1307_CMD_SET_CONTRAST 0x81 // follow with 0x7F
|
||||
#define SSD1307_CMD_DISPLAY_RAM 0xA4
|
||||
#define SSD1307_CMD_DISPLAY_ALLON 0xA5
|
||||
#define SSD1307_CMD_DISPLAY_NORMAL 0xA6
|
||||
#define SSD1307_CMD_DISPLAY_INVERTED 0xA7
|
||||
#define SSD1307_CMD_DISPLAY_OFF 0xAE
|
||||
#define SSD1307_CMD_DISPLAY_ON 0xAF
|
||||
|
||||
// Display Scrolling Parameters
|
||||
#define SSD1307_CMD_RIGHT_HORIZONTAL_SCROLL 0x26 // Init rt scroll
|
||||
#define SSD1307_CMD_LEFT_HORIZONTAL_SCROLL 0x27 // Init left scroll
|
||||
#define SSD1307_CMD_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 // Init diag scroll
|
||||
#define SSD1307_CMD_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A // Init diag scroll
|
||||
#define SSD1307_CMD_DEACTIVATE_SCROLL 0x2E // Stop scroll
|
||||
#define SSD1307_CMD_ACTIVATE_SCROLL 0x2F // Start scroll
|
||||
#define SSD1307_CMD_SET_VERTICAL_SCROLL_AREA 0xA3 // Set scroll range
|
||||
|
||||
// Addressing Command Table (pg.30)
|
||||
#define SSD1307_CMD_SET_LOWER_COLUMN_ADDR 0x00 // Set Lower Column Start Address for Page Addressing Mode, using X[3:0]
|
||||
#define SSD1307_CMD_SET_HIGHER_COLUMN_ADDR 0x10 // Set Higher Column Start Address for Page Addressing Mode, using X[3:0]
|
||||
#define SSD1307_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 00b= Horizontal Addressing Mode; 01b=Vertical Addressing Mode; 10b= Page Addressing Mode (RESET)
|
||||
#define SSD1307_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127
|
||||
#define SSD1307_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7
|
||||
#define SSD1307_CMD_SET_PAGE_ADDR 0xB0
|
||||
|
||||
// Hardware Config (pg.31)
|
||||
#define SSD1307_CMD_SET_DISPLAY_START_LINE 0x40
|
||||
#define SSD1307_CMD_SET_SEGMENT_REMAP 0xA1
|
||||
#define SSD1307_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX
|
||||
#define SSD1307_CMD_SET_COM_SCAN_MODE_NORMAL 0xC0
|
||||
#define SSD1307_CMD_SET_COM_SCAN_MODE_REMAP 0xC8
|
||||
#define SSD1307_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
|
||||
#define SSD1307_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12
|
||||
#define SSD1307_CMD_NOP 0xE3 // NOP
|
||||
|
||||
// Timing and Driving Scheme (pg.32)
|
||||
#define SSD1307_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
|
||||
#define SSD1307_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1
|
||||
#define SSD1307_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30
|
||||
|
||||
// Charge Pump (pg.62)
|
||||
#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14
|
||||
|
||||
#define SSD1307_COLUMN_ADDR 0x00
|
||||
#define SSD1307_LOWER_ADDRESS (SSD1307_CMD_SET_LOWER_COLUMN_ADDR + (SSD1307_COLUMN_ADDR&0x0f))
|
||||
#define SSD1307_HIGHER_ADDRESS (SSD1307_CMD_SET_HIGHER_COLUMN_ADDR + ((SSD1307_COLUMN_ADDR&0xf0)>>4))
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
static esp_err_t lcd_ssd1307_write_ram_data(uint16_t color);
|
||||
|
||||
scr_driver_t lcd_ssd1307_default_driver = {
|
||||
.init = lcd_ssd1307_init,
|
||||
.deinit = lcd_ssd1307_deinit,
|
||||
.set_direction = lcd_ssd1307_set_rotate,
|
||||
.set_window = lcd_ssd1307_set_window,
|
||||
.write_ram_data = lcd_ssd1307_write_ram_data,
|
||||
.draw_pixel = lcd_ssd1307_draw_pixel,
|
||||
.draw_bitmap = lcd_ssd1307_draw_bitmap,
|
||||
.get_info = lcd_ssd1307_get_info,
|
||||
};
|
||||
|
||||
|
||||
esp_err_t lcd_ssd1307_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= SSD1307_COLUMNS, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= SSD1307_HEIGHT, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
LCD_WRITE_CMD(0xAE); //--turn off oled panel
|
||||
LCD_WRITE_CMD(0x00); //---set low column address
|
||||
LCD_WRITE_CMD(0x10); //---set high column address
|
||||
LCD_WRITE_CMD(0x40); //--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
|
||||
LCD_WRITE_CMD(0x81); //--set contrast control register
|
||||
LCD_WRITE_CMD(0xCF); // Set SEG Output Current Brightness
|
||||
LCD_WRITE_CMD(0xA1); //--Set SEG/Column Mapping
|
||||
LCD_WRITE_CMD(0xC0); //Set COM/Row Scan Direction
|
||||
LCD_WRITE_CMD(0xA6); //--set normal display
|
||||
LCD_WRITE_CMD(0xA8); //--set multiplex ratio(1 to 64)
|
||||
LCD_WRITE_CMD(0x3f); //--1/64 duty
|
||||
LCD_WRITE_CMD(0xD3); //-set display offset Shift Mapping RAM Counter (0x00~0x3F)
|
||||
LCD_WRITE_CMD(0x00); //-not offset
|
||||
LCD_WRITE_CMD(0xd5); //--set display clock divide ratio/oscillator frequency
|
||||
LCD_WRITE_CMD(0x80); //--set divide ratio, Set Clock as 100 Frames/Sec
|
||||
LCD_WRITE_CMD(0xD9); //--set pre-charge period
|
||||
LCD_WRITE_CMD(0xF1); //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
|
||||
LCD_WRITE_CMD(0xDA); //--set com pins hardware configuration
|
||||
LCD_WRITE_CMD(0x12);
|
||||
LCD_WRITE_CMD(0xDB); //--set vcomh
|
||||
LCD_WRITE_CMD(0x40); //Set VCOM Deselect Level
|
||||
LCD_WRITE_CMD(0x20); //-Set Page Addressing Mode (0x00/0x01/0x02)
|
||||
LCD_WRITE_CMD(0x02);
|
||||
LCD_WRITE_CMD(0x8D); //--set Charge Pump enable/disable
|
||||
LCD_WRITE_CMD(0x14); //--set(0x10) disable
|
||||
LCD_WRITE_CMD(0xA4); // Disable Entire Display On (0xa4/0xa5)
|
||||
LCD_WRITE_CMD(0xA6); // Disable Inverse Display On (0xa6/a7)
|
||||
LCD_WRITE_CMD(0xAF); //--turn on oled panel
|
||||
|
||||
lcd_ssd1307_set_rotate(lcd_conf->rotate);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_MONO;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_set_rotate(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
ret |= LCD_WRITE_CMD(0xA0);
|
||||
ret |= LCD_WRITE_CMD(0xC0);
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
ret |= LCD_WRITE_CMD(0xA0);
|
||||
ret |= LCD_WRITE_CMD(0xC8);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
ret |= LCD_WRITE_CMD(0xA1);
|
||||
ret |= LCD_WRITE_CMD(0xC0);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
ret |= LCD_WRITE_CMD(0xA1);
|
||||
ret |= LCD_WRITE_CMD(0xC8);
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupport rotate direction");
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
y1 += 1;
|
||||
LCD_CHECK((0 == (y0 % 8)), "y0 should be a multiple of 8", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((0 == (y1 % 8)), "(y1+1) should be a multiple of 8", ESP_ERR_INVALID_ARG);
|
||||
uint8_t row1 = y0 >> 3;
|
||||
uint8_t row2 = y1 / 8;
|
||||
ret |= LCD_WRITE_CMD(SSD1307_CMD_SET_MEMORY_ADDR_MODE);
|
||||
ret |= LCD_WRITE_CMD(0); /**< Set to Horizontal Addressing Mode */
|
||||
ret |= LCD_WRITE_CMD(SSD1307_CMD_SET_COLUMN_RANGE);
|
||||
ret |= LCD_WRITE_CMD(x0);
|
||||
ret |= LCD_WRITE_CMD(x1 - 1);
|
||||
ret |= LCD_WRITE_CMD(SSD1307_CMD_SET_PAGE_RANGE);
|
||||
ret |= LCD_WRITE_CMD(row1);
|
||||
ret |= LCD_WRITE_CMD(row2 - 1);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_ssd1307_write_ram_data(uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "lcd ssd1307 unsupport write ram data");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "SSD1307 not support draw pixel without buffer");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
LCD_CHECK((x + w <= g_lcd_handle.width) && (y + h <= g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t *p = (uint8_t *)bitmap;
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ssd1307_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = LCD_WRITE(p, w * LCD_BPP / 8 * h);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "Draw bitmap failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_display_on(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(OLED_CMD_SET_CHARGE_PUMP); // SET DCDC
|
||||
ret |= LCD_WRITE_CMD(0X14); // Enable charge pump during display on
|
||||
ret |= LCD_WRITE_CMD(SSD1307_CMD_DISPLAY_ON); // DISPLAY ON
|
||||
LCD_CHECK(ESP_OK == ret, "Set display ON failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_display_off(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(OLED_CMD_SET_CHARGE_PUMP); //SET DCDC
|
||||
ret |= LCD_WRITE_CMD(0X10); // Disable charge pump
|
||||
ret |= LCD_WRITE_CMD(SSD1307_CMD_DISPLAY_OFF); //DISPLAY OFF
|
||||
LCD_CHECK(ESP_OK == ret, "Set display OFF failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_set_contrast(uint8_t contrast)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(0x81);
|
||||
ret |= LCD_WRITE_CMD(contrast);
|
||||
LCD_CHECK(ESP_OK == ret, "Set contrast failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_start_horizontal_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval)
|
||||
{
|
||||
LCD_CHECK(start < SSD1307_PAGES, "Start page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(stop < SSD1307_PAGES, "End page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(interval < 8, "Time interval invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
uint8_t cmd = dir ? SSD1307_CMD_LEFT_HORIZONTAL_SCROLL : SSD1307_CMD_RIGHT_HORIZONTAL_SCROLL;
|
||||
LCD_WRITE_CMD(cmd);
|
||||
LCD_WRITE_CMD(0x00);
|
||||
LCD_WRITE_CMD(start);
|
||||
LCD_WRITE_CMD(interval);
|
||||
LCD_WRITE_CMD(stop);
|
||||
|
||||
LCD_WRITE_CMD(0x00);
|
||||
LCD_WRITE_CMD(0xff);
|
||||
LCD_WRITE_CMD(SSD1307_CMD_ACTIVATE_SCROLL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_start_vertical_scroll(uint8_t start, uint8_t stop)
|
||||
{
|
||||
LCD_CHECK(start < SSD1307_PAGES, "Start page address invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(stop < SSD1307_PAGES, "End page address invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_WRITE_CMD(SSD1307_CMD_SET_VERTICAL_SCROLL_AREA);
|
||||
LCD_WRITE_CMD(start);
|
||||
LCD_WRITE_CMD(stop);
|
||||
LCD_WRITE_CMD(SSD1307_CMD_ACTIVATE_SCROLL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_start_scroll_diagRight(uint8_t start, uint8_t stop)
|
||||
{
|
||||
LCD_WRITE_CMD(SSD1307_CMD_SET_VERTICAL_SCROLL_AREA);
|
||||
LCD_WRITE_CMD(0x00);
|
||||
LCD_WRITE_CMD(32);
|
||||
LCD_WRITE_CMD(SSD1307_CMD_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
|
||||
LCD_WRITE_CMD(0x00);
|
||||
LCD_WRITE_CMD(start);
|
||||
LCD_WRITE_CMD(0x00);
|
||||
LCD_WRITE_CMD(stop);
|
||||
LCD_WRITE_CMD(0x01);
|
||||
LCD_WRITE_CMD(SSD1307_CMD_ACTIVATE_SCROLL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1307_stop_scroll(void)
|
||||
{
|
||||
LCD_WRITE_CMD(SSD1307_CMD_DEACTIVATE_SCROLL);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// 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.
|
||||
#ifndef __IOT_LCD_SSD1307_H__
|
||||
#define __IOT_LCD_SSD1307_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
/**
|
||||
* @brief screen initial
|
||||
*
|
||||
* @param lcd_conf configuration struct of ssd1306
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitial screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_set_rotate(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
/**
|
||||
* @brief Set the contrast of screen
|
||||
*
|
||||
* @param contrast Contrast to set
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_set_contrast(uint8_t contrast);
|
||||
|
||||
/**
|
||||
* @brief Turn on the screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_display_on(void);
|
||||
|
||||
/**
|
||||
* @brief Turn off the screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_display_off(void);
|
||||
|
||||
/**
|
||||
* @brief Start horizontal scroll
|
||||
*
|
||||
* @param dir Direction of horizontal scroll
|
||||
* @param start start page
|
||||
* @param stop end page
|
||||
* @param interval time interval of scroll
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_start_horizontal_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_start_vertical_scroll(uint8_t start, uint8_t stop);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_start_scroll_diagRight(uint8_t start, uint8_t stop);
|
||||
|
||||
/**
|
||||
* @brief Stop screen scroll
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1307_stop_scroll(void);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,297 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ssd1322.h"
|
||||
|
||||
static const char *TAG = "lcd ssd1322";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
// Some fundamental define for screen controller
|
||||
#define SSD1322_HEIGHT 128
|
||||
#define SSD1322_WIDTH 480
|
||||
#define SSD1322_COLUMNS 120
|
||||
#define SSD1322_BITS_PER_PIXEL 4
|
||||
|
||||
// Commands
|
||||
#define SSD1322_SETCOMMANDLOCK 0xFD
|
||||
#define SSD1322_DISPLAYOFF 0xAE
|
||||
#define SSD1322_DISPLAYON 0xAF
|
||||
#define SSD1322_SETCLOCKDIVIDER 0xB3
|
||||
#define SSD1322_SETDISPLAYOFFSET 0xA2
|
||||
#define SSD1322_SETSTARTLINE 0xA1
|
||||
#define SSD1322_SETREMAP 0xA0
|
||||
#define SSD1322_FUNCTIONSEL 0xAB
|
||||
#define SSD1322_DISPLAYENHANCE 0xB4
|
||||
#define SSD1322_SETCONTRASTCURRENT 0xC1
|
||||
#define SSD1322_MASTERCURRENTCONTROL 0xC7
|
||||
#define SSD1322_SETPHASELENGTH 0xB1
|
||||
#define SSD1322_DISPLAYENHANCEB 0xD1
|
||||
#define SSD1322_SETPRECHARGEVOLTAGE 0xBB
|
||||
#define SSD1322_SETSECONDPRECHARGEPERIOD 0xB6
|
||||
#define SSD1322_SETVCOMH 0xBE
|
||||
#define SSD1322_SETMUXRATIO 0xCA
|
||||
#define SSD1322_SETCOLUMNADDR 0x15
|
||||
#define SSD1322_SETROWADDR 0x75
|
||||
#define SSD1322_WRITERAM 0x5C
|
||||
#define SSD1322_ENTIREDISPLAYOFF 0xA4
|
||||
#define SSD1322_ENTIREDISPLAYON 0xA5
|
||||
#define SSD1322_NORMALDISPLAY 0xA6
|
||||
#define SSD1322_INVERSEDISPLAY 0xA7
|
||||
#define SSD1322_SETGPIO 0xB5
|
||||
#define SSD1322_EXITPARTIALDISPLAY 0xA9
|
||||
#define SSD1322_SELECTDEFAULTGRAYSCALE 0xB9
|
||||
|
||||
#define LCD_NAME "OLED SSD1322"
|
||||
#define LCD_BPP SSD1322_BITS_PER_PIXEL
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
static esp_err_t lcd_ssd1322_write_ram_data(uint16_t color);
|
||||
|
||||
scr_driver_t lcd_ssd1322_default_driver = {
|
||||
.init = lcd_ssd1322_init,
|
||||
.deinit = lcd_ssd1322_deinit,
|
||||
.set_direction = lcd_ssd1322_set_rotate,
|
||||
.set_window = lcd_ssd1322_set_window,
|
||||
.write_ram_data = lcd_ssd1322_write_ram_data,
|
||||
.draw_pixel = lcd_ssd1322_draw_pixel,
|
||||
.draw_bitmap = lcd_ssd1322_draw_bitmap,
|
||||
.get_info = lcd_ssd1322_get_info,
|
||||
};
|
||||
|
||||
esp_err_t lcd_ssd1322_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= SSD1322_WIDTH, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= SSD1322_HEIGHT, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETCOMMANDLOCK);// 0xFD
|
||||
LCD_WRITE_DATA(0x12);// Unlock OLED driver IC
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_DISPLAYOFF);// 0xAE
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETCLOCKDIVIDER);// 0xB3
|
||||
LCD_WRITE_DATA(0x91);// 0xB3
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETMUXRATIO);// 0xCA
|
||||
LCD_WRITE_DATA(0x3F);// duty = 1/64
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETDISPLAYOFFSET);// 0xA2
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETSTARTLINE);// 0xA1
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETREMAP);// 0xA0
|
||||
LCD_WRITE_DATA(0x14);//Horizontal address increment,Disable Column Address Re-map,Enable Nibble Re-map,Scan from COM[N-1] to COM0,Disable COM Split Odd Even
|
||||
LCD_WRITE_DATA(0x11);//Enable Dual COM mode
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETGPIO);// 0xB5
|
||||
LCD_WRITE_DATA(0x00);// Disable GPIO Pins Input
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_FUNCTIONSEL);// 0xAB
|
||||
LCD_WRITE_DATA(0x01);// selection external vdd
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_DISPLAYENHANCE);// 0xB4
|
||||
LCD_WRITE_DATA(0xA0);// enables the external VSL
|
||||
LCD_WRITE_DATA(0xFD);// 0xfFD,Enhanced low GS display quality;default is 0xb5(normal),
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETCONTRASTCURRENT);// 0xC1
|
||||
LCD_WRITE_DATA(0xFF);// 0xFF - default is 0x7f
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_MASTERCURRENTCONTROL);// 0xC7
|
||||
LCD_WRITE_DATA(0x0F);// default is 0x0F
|
||||
|
||||
// Set grayscale
|
||||
LCD_WRITE_CMD(SSD1322_SELECTDEFAULTGRAYSCALE); // 0xB9
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETPHASELENGTH);// 0xB1
|
||||
LCD_WRITE_DATA(0xE2);// default is 0x74
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_DISPLAYENHANCEB);// 0xD1
|
||||
LCD_WRITE_DATA(0x82);// Reserved;default is 0xa2(normal)
|
||||
LCD_WRITE_DATA(0x20);//
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETPRECHARGEVOLTAGE);// 0xBB
|
||||
LCD_WRITE_DATA(0x1F);// 0.6xVcc
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETSECONDPRECHARGEPERIOD);// 0xB6
|
||||
LCD_WRITE_DATA(0x08);// default
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_SETVCOMH);// 0xBE
|
||||
LCD_WRITE_DATA(0x07);// 0.86xVcc;default is 0x04
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_NORMALDISPLAY);// 0xA6
|
||||
|
||||
LCD_WRITE_CMD(SSD1322_EXITPARTIALDISPLAY);// 0xA9
|
||||
LCD_WRITE_CMD(SSD1322_DISPLAYON); //Sleep Out
|
||||
|
||||
lcd_ssd1322_set_rotate(lcd_conf->rotate);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_GRAY;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_set_rotate(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
uint8_t reg_data = 0x04;
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
reg_data |= 0x00;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= 0x10;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= 0x02;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= 0x12;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupport rotate direction");
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_LOGI(TAG, "Set rotate 0x%x", reg_data);
|
||||
ret |= LCD_WRITE_CMD(SSD1322_SETREMAP);
|
||||
ret |= LCD_WRITE_DATA(reg_data);
|
||||
ret |= LCD_WRITE_DATA(0x11);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
x1 += 1;
|
||||
LCD_CHECK((0 == (x0 % 4)), "x0 should be a multiple of 4", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((0 == (x1 % 4)), "(x1+1) should be a multiple of 4", ESP_ERR_INVALID_ARG);
|
||||
uint8_t col0 = x0 / 4;
|
||||
uint8_t col1 = x1 / 4;
|
||||
ret |= LCD_WRITE_CMD(SSD1322_SETCOLUMNADDR);
|
||||
ret |= LCD_WRITE_DATA(0x1c + col0);
|
||||
ret |= LCD_WRITE_DATA(0x1c + col1 - 1);
|
||||
ret |= LCD_WRITE_CMD(SSD1322_SETROWADDR);
|
||||
ret |= LCD_WRITE_DATA(y0);
|
||||
ret |= LCD_WRITE_DATA(y1);
|
||||
ret |= LCD_WRITE_CMD(SSD1322_WRITERAM);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_ssd1322_write_ram_data(uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupport write ram data");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupport draw pixel without buffer");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
LCD_CHECK((x + w <= g_lcd_handle.width) && (y + h <= g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t *p = (uint8_t *)bitmap;
|
||||
|
||||
ret = lcd_ssd1322_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ret = LCD_WRITE(p, w * LCD_BPP / 8 * h);
|
||||
LCD_CHECK(ESP_OK == ret, "Draw bitmap failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_set_contrast(uint8_t contrast)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(SSD1322_SETCONTRASTCURRENT);
|
||||
ret |= LCD_WRITE_DATA(contrast);
|
||||
LCD_CHECK(ESP_OK == ret, "Set contrast failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1322_set_invert(uint8_t is_invert)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(is_invert ? SSD1322_INVERSEDISPLAY : SSD1322_NORMALDISPLAY);
|
||||
LCD_CHECK(ESP_OK == ret, "Set contrast failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
#ifndef __IOT_LCD_SSD1322_H__
|
||||
#define __IOT_LCD_SSD1322_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief device initialization
|
||||
*
|
||||
* @param lcd_conf configuration struct of ssd1306
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitial screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @note Only the first four directions defined by scr_dir_t are supported
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_set_rotate(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
/**
|
||||
* @brief Set the contrast of screen
|
||||
*
|
||||
* @param contrast Contrast to set
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_set_contrast(uint8_t contrast);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1322_set_invert(uint8_t is_invert);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,316 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ssd1351.h"
|
||||
|
||||
static const char *TAG = "oled ssd1351";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "OLED SSD1351"
|
||||
#define LCD_BPP 16
|
||||
|
||||
#define SSD1351_RESOLUTION_HOR 128
|
||||
#define SSD1351_RESOLUTION_VER 128
|
||||
|
||||
/** commands */
|
||||
#define SSD1351_CMD_SETCOLUMN 0x15 ///< See datasheet
|
||||
#define SSD1351_CMD_SETROW 0x75 ///< See datasheet
|
||||
#define SSD1351_CMD_WRITERAM 0x5C ///< See datasheet
|
||||
#define SSD1351_CMD_READRAM 0x5D ///< Not currently used
|
||||
#define SSD1351_CMD_SETREMAP 0xA0 ///< See datasheet
|
||||
#define SSD1351_CMD_STARTLINE 0xA1 ///< See datasheet
|
||||
#define SSD1351_CMD_DISPLAYOFFSET 0xA2 ///< See datasheet
|
||||
#define SSD1351_CMD_DISPLAYALLOFF 0xA4 ///< Not currently used
|
||||
#define SSD1351_CMD_DISPLAYALLON 0xA5 ///< Not currently used
|
||||
#define SSD1351_CMD_NORMALDISPLAY 0xA6 ///< See datasheet
|
||||
#define SSD1351_CMD_INVERTDISPLAY 0xA7 ///< See datasheet
|
||||
#define SSD1351_CMD_FUNCTIONSELECT 0xAB ///< See datasheet
|
||||
#define SSD1351_CMD_DISPLAYOFF 0xAE ///< See datasheet
|
||||
#define SSD1351_CMD_DISPLAYON 0xAF ///< See datasheet
|
||||
#define SSD1351_CMD_PRECHARGE 0xB1 ///< See datasheet
|
||||
#define SSD1351_CMD_DISPLAYENHANCE 0xB2 ///< Not currently used
|
||||
#define SSD1351_CMD_CLOCKDIV 0xB3 ///< See datasheet
|
||||
#define SSD1351_CMD_SETVSL 0xB4 ///< See datasheet
|
||||
#define SSD1351_CMD_SETGPIO 0xB5 ///< See datasheet
|
||||
#define SSD1351_CMD_PRECHARGE2 0xB6 ///< See datasheet
|
||||
#define SSD1351_CMD_SETGRAY 0xB8 ///< Not currently used
|
||||
#define SSD1351_CMD_USELUT 0xB9 ///< Not currently used
|
||||
#define SSD1351_CMD_PRECHARGELEVEL 0xBB ///< Not currently used
|
||||
#define SSD1351_CMD_VCOMH 0xBE ///< See datasheet
|
||||
#define SSD1351_CMD_CONTRASTABC 0xC1 ///< See datasheet
|
||||
#define SSD1351_CMD_CONTRASTMASTER 0xC7 ///< See datasheet
|
||||
#define SSD1351_CMD_MUXRATIO 0xCA ///< See datasheet
|
||||
#define SSD1351_CMD_COMMANDLOCK 0xFD ///< See datasheet
|
||||
#define SSD1351_CMD_HORIZSCROLL 0x96 ///< Not currently used
|
||||
#define SSD1351_CMD_STOPSCROLL 0x9E ///< Not currently used
|
||||
#define SSD1351_CMD_STARTSCROLL 0x9F ///< Not currently used
|
||||
|
||||
#define MADCTL_MY 0x10
|
||||
#define MADCTL_MX 0x02
|
||||
#define MADCTL_MV 0x01
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ssd1351_default_driver = {
|
||||
.init = lcd_ssd1351_init,
|
||||
.deinit = lcd_ssd1351_deinit,
|
||||
.set_direction = lcd_ssd1351_set_rotation,
|
||||
.set_window = lcd_ssd1351_set_window,
|
||||
.write_ram_data = lcd_ssd1351_write_ram_data,
|
||||
.draw_pixel = lcd_ssd1351_draw_pixel,
|
||||
.draw_bitmap = lcd_ssd1351_draw_bitmap,
|
||||
.get_info = lcd_ssd1351_get_info,
|
||||
};
|
||||
|
||||
esp_err_t lcd_ssd1351_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= SSD1351_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= SSD1351_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
LCD_WRITE_CMD(SSD1351_CMD_COMMANDLOCK);// Set command lock, 1 arg
|
||||
LCD_WRITE_DATA(0x12);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_COMMANDLOCK);// Set command lock, 1 arg
|
||||
LCD_WRITE_DATA(0xB1);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_DISPLAYOFF);// Display off, no args
|
||||
LCD_WRITE_CMD(SSD1351_CMD_CLOCKDIV);
|
||||
LCD_WRITE_DATA(0xF1); // 7:4 = Oscillator Freq, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16)
|
||||
LCD_WRITE_CMD(SSD1351_CMD_MUXRATIO);
|
||||
LCD_WRITE_DATA(127);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_DISPLAYOFFSET);
|
||||
LCD_WRITE_DATA(0x0);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_SETGPIO);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_FUNCTIONSELECT);
|
||||
LCD_WRITE_DATA(0x01); // internal (diode drop)
|
||||
LCD_WRITE_CMD(SSD1351_CMD_PRECHARGE);
|
||||
LCD_WRITE_DATA(0x32);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_VCOMH);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_NORMALDISPLAY);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_CONTRASTABC);
|
||||
LCD_WRITE_DATA(0xC8);
|
||||
LCD_WRITE_DATA(0x80);
|
||||
LCD_WRITE_DATA(0xC8);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_CONTRASTMASTER);
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_SETVSL);
|
||||
LCD_WRITE_DATA(0xA0);
|
||||
LCD_WRITE_DATA(0xB5);
|
||||
LCD_WRITE_DATA(0x55);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_PRECHARGE2);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_CMD(SSD1351_CMD_DISPLAYON);
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
return lcd_ssd1351_set_rotation(lcd_conf->rotate);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
|
||||
/**
|
||||
* 6,7 Color depth (01 = 65K)
|
||||
* 5 Odd/even split COM (0: disable, 1: enable)
|
||||
* 4 Scan direction (0: top-down, 1: bottom-up)
|
||||
* 3 Reserved
|
||||
* 2 Color remap (0: A->B->C, 1: C->B->A)
|
||||
* 1 Column remap (0: 0-127, 1: 127-0)
|
||||
* 0 Address increment (0: horizontal, 1: vertical)
|
||||
*/
|
||||
uint8_t reg_data = 0b01100100;
|
||||
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=%x", reg_data);
|
||||
ret = LCD_WRITE_CMD(SSD1351_CMD_SETREMAP);
|
||||
ret |= LCD_WRITE_DATA(reg_data);
|
||||
// uint8_t startline = (dir < 2) ? g_lcd_handle.original_height : 0;
|
||||
// ret |= LCD_WRITE_CMD(SSD1351_CMD_STARTLINE);
|
||||
// ret |= LCD_WRITE_DATA(startline);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, SSD1351_RESOLUTION_HOR, SSD1351_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(SSD1351_CMD_SETCOLUMN);
|
||||
ret |= LCD_WRITE_DATA(x0);
|
||||
ret |= LCD_WRITE_DATA(x1);
|
||||
ret |= LCD_WRITE_CMD(SSD1351_CMD_SETROW);
|
||||
ret |= LCD_WRITE_DATA(y0);
|
||||
ret |= LCD_WRITE_DATA(y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(SSD1351_CMD_WRITERAM);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_set_invert(bool is_invert)
|
||||
{
|
||||
return LCD_WRITE_CMD(is_invert ? SSD1351_CMD_INVERTDISPLAY : SSD1351_CMD_NORMALDISPLAY);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t lcd_ssd1351_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ssd1351_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ssd1351_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1351_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ssd1351_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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.
|
||||
#ifndef _IOT_LCD_SSD1351_H_
|
||||
#define _IOT_LCD_SSD1351_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_set_invert(bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1351_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,345 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "ssd1963.h"
|
||||
|
||||
static const char *TAG = "lcd ssd1963";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "SSD1963"
|
||||
#define LCD_BPP 16
|
||||
|
||||
#define SSD1963_CASET 0x2A
|
||||
#define SSD1963_RASET 0x2B
|
||||
#define SSD1963_RAMWR 0x2C
|
||||
#define SSD1963_MADCTL 0x36
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x01
|
||||
#define MADCTL_MX 0x02
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define SSD1963_RESOLUTION_HOR 800
|
||||
#define SSD1963_RESOLUTION_VER 480
|
||||
|
||||
//LCD panel configuration
|
||||
#define SSD_HOR_PULSE_WIDTH 1
|
||||
#define SSD_HOR_BACK_PORCH 46
|
||||
#define SSD_HOR_FRONT_PORCH 210
|
||||
|
||||
#define SSD_VER_PULSE_WIDTH 1
|
||||
#define SSD_VER_BACK_PORCH 23
|
||||
#define SSD_VER_FRONT_PORCH 22
|
||||
|
||||
#define SSD_HT (SSD1963_RESOLUTION_HOR+SSD_HOR_BACK_PORCH+SSD_HOR_FRONT_PORCH)
|
||||
#define SSD_HPS (SSD_HOR_BACK_PORCH)
|
||||
#define SSD_VT (SSD1963_RESOLUTION_VER+SSD_VER_BACK_PORCH+SSD_VER_FRONT_PORCH)
|
||||
#define SSD_VPS (SSD_VER_BACK_PORCH)
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_ssd1963_default_driver = {
|
||||
.init = lcd_ssd1963_init,
|
||||
.deinit = lcd_ssd1963_deinit,
|
||||
.set_direction = lcd_ssd1963_set_rotation,
|
||||
.set_window = lcd_ssd1963_set_window,
|
||||
.write_ram_data = lcd_ssd1963_write_ram_data,
|
||||
.draw_pixel = lcd_ssd1963_draw_pixel,
|
||||
.draw_bitmap = lcd_ssd1963_draw_bitmap,
|
||||
.get_info = lcd_ssd1963_get_info,
|
||||
};
|
||||
|
||||
static void lcd_ssd1963_init_reg(void);
|
||||
|
||||
esp_err_t lcd_ssd1963_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= SSD1963_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= SSD1963_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_ssd1963_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
|
||||
ret = lcd_ssd1963_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "set rotation failed", ESP_FAIL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data &= ~MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=0x%x", reg_data);
|
||||
ret = LCD_WRITE_REG(SSD1963_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, SSD1963_RESOLUTION_HOR, SSD1963_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
LCD_WRITE_CMD(SSD1963_CASET);
|
||||
LCD_WRITE_DATA(x0 >> 8);
|
||||
LCD_WRITE_DATA(x0 & 0XFF);
|
||||
LCD_WRITE_DATA(x1 >> 8);
|
||||
LCD_WRITE_DATA(x1 & 0XFF);
|
||||
LCD_WRITE_CMD(SSD1963_RASET);
|
||||
LCD_WRITE_DATA(y0 >> 8);
|
||||
LCD_WRITE_DATA(y0 & 0XFF);
|
||||
LCD_WRITE_DATA(y1 >> 8);
|
||||
LCD_WRITE_DATA(y1 & 0XFF);
|
||||
|
||||
ret |= LCD_WRITE_CMD(SSD1963_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_ssd1963_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_ssd1963_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_ssd1963_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
LCD_CHECK((x + w <= g_lcd_handle.width) && (y + h <= g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t *p = (uint8_t *)bitmap;
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_ssd1963_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = LCD_WRITE(p, w * LCD_BPP / 8 * h);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "Draw bitmap failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void lcd_ssd1963_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0xE2); //Set PLL with OSC = 10MHz (hardware), Multiplier N = 35, 250MHz < VCO < 800MHz = OSC*(N+1), VCO = 300MHz
|
||||
LCD_WRITE_DATA(0x1D); //
|
||||
LCD_WRITE_DATA(0x02); //Divider M = 2, PLL = 300/(M+1) = 100MHz
|
||||
LCD_WRITE_DATA(0x04); //Validate M and N values
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
|
||||
LCD_WRITE_CMD(0xE0); // Start PLL command
|
||||
LCD_WRITE_DATA(0x01); // enable PLL
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
LCD_WRITE_CMD(0xE0); // Start PLL command again
|
||||
LCD_WRITE_DATA(0x03); // now, use PLL output as system clock
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(12));
|
||||
|
||||
LCD_WRITE_CMD(0x01); //soft-reset
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
LCD_WRITE_CMD(0xE6); //set pixel frequency,33Mhz
|
||||
LCD_WRITE_DATA(0x2F);
|
||||
LCD_WRITE_DATA(0xFF);
|
||||
LCD_WRITE_DATA(0xFF);
|
||||
|
||||
LCD_WRITE_CMD(0xB0); //set LCD mode
|
||||
LCD_WRITE_DATA(0x20); //24-bit mode
|
||||
LCD_WRITE_DATA(0x00); //TFT
|
||||
LCD_WRITE_DATA((SSD1963_RESOLUTION_HOR - 1) >> 8); //set LCD horizontal pixel number
|
||||
LCD_WRITE_DATA(SSD1963_RESOLUTION_HOR - 1);
|
||||
LCD_WRITE_DATA((SSD1963_RESOLUTION_VER - 1) >> 8); //set LCD vertical pixel number
|
||||
LCD_WRITE_DATA(SSD1963_RESOLUTION_VER - 1);
|
||||
LCD_WRITE_DATA(0x00); //RGB
|
||||
|
||||
LCD_WRITE_CMD(0xB4); //Set horizontal period
|
||||
LCD_WRITE_DATA((SSD_HT - 1) >> 8);
|
||||
LCD_WRITE_DATA(SSD_HT - 1);
|
||||
LCD_WRITE_DATA(SSD_HPS >> 8);
|
||||
LCD_WRITE_DATA(SSD_HPS);
|
||||
LCD_WRITE_DATA(SSD_HOR_PULSE_WIDTH - 1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0xB6); //Set vertical period
|
||||
LCD_WRITE_DATA((SSD_VT - 1) >> 8);
|
||||
LCD_WRITE_DATA(SSD_VT - 1);
|
||||
LCD_WRITE_DATA(SSD_VPS >> 8);
|
||||
LCD_WRITE_DATA(SSD_VPS);
|
||||
LCD_WRITE_DATA(SSD_VER_FRONT_PORCH - 1);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
|
||||
LCD_WRITE_CMD(0xF0); //set SSD1963 interface is 16bit
|
||||
LCD_WRITE_DATA(0x03); //16-bit(565 format) data for 16bpp
|
||||
|
||||
LCD_WRITE_CMD(0x29); //display on
|
||||
|
||||
LCD_WRITE_CMD(0xD0);
|
||||
LCD_WRITE_DATA(0x00); //disable
|
||||
|
||||
LCD_WRITE_CMD(0xBE); //configuration PWM output
|
||||
LCD_WRITE_DATA(0x05); //1 PWM frequency
|
||||
LCD_WRITE_DATA(0xFE); //2 PWM duty
|
||||
LCD_WRITE_DATA(0x01); //3 C
|
||||
LCD_WRITE_DATA(0x00); //4 D
|
||||
LCD_WRITE_DATA(0x00); //5 E
|
||||
LCD_WRITE_DATA(0x00); //6 F
|
||||
|
||||
LCD_WRITE_CMD(0xB8); //set GPIO
|
||||
LCD_WRITE_DATA(0x03);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
|
||||
LCD_WRITE_CMD(0xBA);
|
||||
LCD_WRITE_DATA(0X01); //GPIO[1:0]=01
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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.
|
||||
#ifndef __LCD_SSD1963_H__
|
||||
#define __LCD_SSD1963_H__
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief device initialization
|
||||
*
|
||||
* @param lcd_conf configuration struct of ssd1963
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitial screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t lcd_ssd1963_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,332 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "st7789.h"
|
||||
|
||||
static const char *TAG = "lcd st7789";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define LCD_NAME "ST7789"
|
||||
#define LCD_BPP 16
|
||||
|
||||
/** commands of ST7789 */
|
||||
#define LCD_SWRESET 0x01 // Software Reset
|
||||
#define LCD_RDDID 0x04 // Read Display ID
|
||||
#define LCD_INVOFF 0x20 // Display Inversion Off
|
||||
#define LCD_INVON 0x21 // Display Inversion On
|
||||
#define LCD_CASET 0x2A // Column Address Set
|
||||
#define LCD_PASET 0x2B // Row Address Set
|
||||
#define LCD_RAMWR 0x2C // Memory Writ
|
||||
#define LCD_RAMRD 0x2E // Memory Read
|
||||
#define LCD_MADCTL 0x36 // Memory Data Access Control
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
#define ST7789_RESOLUTION_HOR 240
|
||||
#define ST7789_RESOLUTION_VER 320
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_st7789_default_driver = {
|
||||
.init = lcd_st7789_init,
|
||||
.deinit = lcd_st7789_deinit,
|
||||
.set_direction = lcd_st7789_set_rotation,
|
||||
.set_window = lcd_st7789_set_window,
|
||||
.write_ram_data = lcd_st7789_write_ram_data,
|
||||
.draw_pixel = lcd_st7789_draw_pixel,
|
||||
.draw_bitmap = lcd_st7789_draw_bitmap,
|
||||
.get_info = lcd_st7789_get_info,
|
||||
};
|
||||
|
||||
static void lcd_st7789_init_reg(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x3A);
|
||||
LCD_WRITE_DATA(0x05);
|
||||
|
||||
LCD_WRITE_CMD(0xB2);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x33);
|
||||
LCD_WRITE_DATA(0x33);
|
||||
|
||||
LCD_WRITE_CMD(0xB7); //Gate Control
|
||||
LCD_WRITE_DATA(0x35);
|
||||
|
||||
LCD_WRITE_CMD(0xBB); //VCOM Setting
|
||||
LCD_WRITE_DATA(0x19);
|
||||
|
||||
LCD_WRITE_CMD(0xC0); //LCM Control
|
||||
LCD_WRITE_DATA(0x2C);
|
||||
|
||||
LCD_WRITE_CMD(0xC2); //VDV and VRH Command Enable
|
||||
LCD_WRITE_DATA(0x01);
|
||||
LCD_WRITE_CMD(0xC3); //VRH Set
|
||||
LCD_WRITE_DATA(0x12);
|
||||
LCD_WRITE_CMD(0xC4); //VDV Set
|
||||
LCD_WRITE_DATA(0x20);
|
||||
|
||||
LCD_WRITE_CMD(0xC6); //Frame Rate Control in Normal Mode
|
||||
LCD_WRITE_DATA(0x0F);
|
||||
|
||||
LCD_WRITE_CMD(0xD0); // Power Control 1
|
||||
LCD_WRITE_DATA(0xA4);
|
||||
LCD_WRITE_DATA(0xA1);
|
||||
|
||||
LCD_WRITE_CMD(0xE0); //Positive Voltage Gamma Control
|
||||
LCD_WRITE_DATA(0xD0);
|
||||
LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x11);
|
||||
LCD_WRITE_DATA(0x13);
|
||||
LCD_WRITE_DATA(0x2B);
|
||||
LCD_WRITE_DATA(0x3F);
|
||||
LCD_WRITE_DATA(0x54);
|
||||
LCD_WRITE_DATA(0x4C);
|
||||
LCD_WRITE_DATA(0x18);
|
||||
LCD_WRITE_DATA(0x0D);
|
||||
LCD_WRITE_DATA(0x0B);
|
||||
LCD_WRITE_DATA(0x1F);
|
||||
LCD_WRITE_DATA(0x23);
|
||||
|
||||
LCD_WRITE_CMD(0xE1); //Negative Voltage Gamma Control
|
||||
LCD_WRITE_DATA(0xD0);
|
||||
LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x0C);
|
||||
LCD_WRITE_DATA(0x11);
|
||||
LCD_WRITE_DATA(0x13);
|
||||
LCD_WRITE_DATA(0x2C);
|
||||
LCD_WRITE_DATA(0x3F);
|
||||
LCD_WRITE_DATA(0x44);
|
||||
LCD_WRITE_DATA(0x51);
|
||||
LCD_WRITE_DATA(0x2F);
|
||||
LCD_WRITE_DATA(0x1F);
|
||||
LCD_WRITE_DATA(0x1F);
|
||||
LCD_WRITE_DATA(0x20);
|
||||
LCD_WRITE_DATA(0x23);
|
||||
|
||||
LCD_WRITE_CMD(0x21); //Display Inversion On
|
||||
|
||||
LCD_WRITE_CMD(0x11); //Sleep Out
|
||||
|
||||
LCD_WRITE_CMD(0x29); //Display On
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ST7789_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ST7789_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
lcd_st7789_init_reg();
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
ret = lcd_st7789_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "Set rotate failed", ESP_FAIL);
|
||||
ret = lcd_st7789_set_invert(1); /**< ST7789 setting the reverse color is the normal color */
|
||||
LCD_CHECK(ESP_OK == ret, "Set color invert failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data &= ~MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=%x", reg_data);
|
||||
ret = LCD_WRITE_REG(LCD_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ST7789_RESOLUTION_HOR, ST7789_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(LCD_PASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_set_invert(bool is_invert)
|
||||
{
|
||||
return LCD_WRITE_CMD(is_invert ? LCD_INVON : LCD_INVOFF);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_st7789_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_st7789_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7789_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_st7789_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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.
|
||||
#ifndef _IOT_LCD_ST7789_H_
|
||||
#define _IOT_LCD_ST7789_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_st7789_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_set_invert(bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7789_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "screen/screen_driver.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
#include "st7796.h"
|
||||
|
||||
static const char *TAG = "lcd st7796";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
|
||||
#define LCD_NAME "ST7796"
|
||||
#define LCD_BPP 16
|
||||
|
||||
#define ST7796_RESOLUTION_HOR 320
|
||||
#define ST7796_RESOLUTION_VER 480
|
||||
|
||||
/** commands of ST7796 */
|
||||
#define LCD_SWRESET 0x01 // Software Reset
|
||||
#define LCD_RDDID 0x04 // Read Display ID
|
||||
#define LCD_INVOFF 0x20 // Display Inversion Off
|
||||
#define LCD_INVON 0x21 // Display Inversion On
|
||||
#define LCD_CASET 0x2A // Column Address Set
|
||||
#define LCD_PASET 0x2B // Row Address Set
|
||||
#define LCD_RAMWR 0x2C // Memory Writ
|
||||
#define LCD_RAMRD 0x2E // Memory Read
|
||||
#define LCD_MADCTL 0x36 // Memory Data Access Control
|
||||
|
||||
/* MADCTL Defines */
|
||||
#define MADCTL_MY 0x80
|
||||
#define MADCTL_MX 0x40
|
||||
#define MADCTL_MV 0x20
|
||||
#define MADCTL_ML 0x10
|
||||
#define MADCTL_RGB 0x08
|
||||
#define MADCTL_MH 0x04
|
||||
|
||||
static scr_handle_t g_lcd_handle;
|
||||
|
||||
/**
|
||||
* This header file is only used to redefine the function to facilitate the call.
|
||||
* It can only be placed in this position, not in the head of the file.
|
||||
*/
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
scr_driver_t lcd_st7796_default_driver = {
|
||||
.init = lcd_st7796_init,
|
||||
.deinit = lcd_st7796_deinit,
|
||||
.set_direction = lcd_st7796_set_rotation,
|
||||
.set_window = lcd_st7796_set_window,
|
||||
.write_ram_data = lcd_st7796_write_ram_data,
|
||||
.draw_pixel = lcd_st7796_draw_pixel,
|
||||
.draw_bitmap = lcd_st7796_draw_bitmap,
|
||||
.get_info = lcd_st7796_get_info,
|
||||
};
|
||||
|
||||
|
||||
static esp_err_t lcd_st7796_reg_config(void);
|
||||
|
||||
esp_err_t lcd_st7796_init(const scr_controller_config_t *lcd_conf)
|
||||
{
|
||||
LCD_CHECK(lcd_conf->width <= ST7796_RESOLUTION_HOR, "Width greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(lcd_conf->height <= ST7796_RESOLUTION_VER, "Height greater than maximum", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK(NULL != lcd_conf, "config pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((NULL != lcd_conf->interface_drv->write_cmd && \
|
||||
NULL != lcd_conf->interface_drv->write_data && \
|
||||
NULL != lcd_conf->interface_drv->write && \
|
||||
NULL != lcd_conf->interface_drv->read && \
|
||||
NULL != lcd_conf->interface_drv->bus_acquire && \
|
||||
NULL != lcd_conf->interface_drv->bus_release),
|
||||
"Interface driver invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret;
|
||||
// Reset the display
|
||||
if (lcd_conf->pin_num_rst >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_rst);
|
||||
gpio_set_direction(lcd_conf->pin_num_rst, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (lcd_conf->rst_active_level) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
gpio_set_level(lcd_conf->pin_num_rst, (~(lcd_conf->rst_active_level)) & 0x1);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
g_lcd_handle.interface_drv = lcd_conf->interface_drv;
|
||||
g_lcd_handle.original_width = lcd_conf->width;
|
||||
g_lcd_handle.original_height = lcd_conf->height;
|
||||
g_lcd_handle.offset_hor = lcd_conf->offset_hor;
|
||||
g_lcd_handle.offset_ver = lcd_conf->offset_ver;
|
||||
|
||||
// Send all the commands
|
||||
ret = lcd_st7796_reg_config();
|
||||
LCD_CHECK(ESP_OK == ret, "Write lcd register encounter error", ESP_FAIL);
|
||||
|
||||
// Enable backlight
|
||||
if (lcd_conf->pin_num_bckl >= 0) {
|
||||
gpio_pad_select_gpio(lcd_conf->pin_num_bckl);
|
||||
gpio_set_direction(lcd_conf->pin_num_bckl, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(lcd_conf->pin_num_bckl, (lcd_conf->bckl_active_level) & 0x1);
|
||||
}
|
||||
ret = lcd_st7796_set_rotation(lcd_conf->rotate);
|
||||
LCD_CHECK(ESP_OK == ret, "Set rotate failed", ESP_FAIL);
|
||||
lcd_st7796_set_invert(false);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_deinit(void)
|
||||
{
|
||||
memset(&g_lcd_handle, 0, sizeof(scr_handle_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_set_rotation(scr_dir_t dir)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_data = 0;
|
||||
reg_data |= MADCTL_RGB;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
LCD_CHECK(dir < 8, "Unsupport rotate direction", ESP_ERR_INVALID_ARG);
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
reg_data |= MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
reg_data |= MADCTL_MX;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY;
|
||||
g_lcd_handle.width = g_lcd_handle.original_width;
|
||||
g_lcd_handle.height = g_lcd_handle.original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
reg_data |= MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
reg_data |= MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
reg_data |= MADCTL_MX | MADCTL_MY | MADCTL_MV;
|
||||
g_lcd_handle.width = g_lcd_handle.original_height;
|
||||
g_lcd_handle.height = g_lcd_handle.original_width;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGI(TAG, "MADCTL=%x", reg_data);
|
||||
ret = LCD_WRITE_REG(LCD_MADCTL, reg_data);
|
||||
LCD_CHECK(ESP_OK == ret, "Set screen rotate failed", ESP_FAIL);
|
||||
g_lcd_handle.dir = dir;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_get_info(scr_info_t *info)
|
||||
{
|
||||
LCD_CHECK(NULL != info, "info pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
info->width = g_lcd_handle.width;
|
||||
info->height = g_lcd_handle.height;
|
||||
info->dir = g_lcd_handle.dir;
|
||||
info->name = LCD_NAME;
|
||||
info->color_type = SCR_COLOR_TYPE_RGB565;
|
||||
info->bpp = LCD_BPP;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
|
||||
{
|
||||
LCD_CHECK((x1 < g_lcd_handle.width) && (y1 < g_lcd_handle.height), "The set coordinates exceed the screen size", ESP_ERR_INVALID_ARG);
|
||||
LCD_CHECK((x0 <= x1) && (y0 <= y1), "Window coordinates invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
scr_utility_apply_offset(&g_lcd_handle, ST7796_RESOLUTION_HOR, ST7796_RESOLUTION_VER, &x0, &y0, &x1, &y1);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_CASET);
|
||||
ret |= LCD_WRITE_DATA(x0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(x1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(x1 & 0xff);
|
||||
ret |= LCD_WRITE_CMD(LCD_PASET);
|
||||
ret |= LCD_WRITE_DATA(y0 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y0 & 0xff);
|
||||
ret |= LCD_WRITE_DATA(y1 >> 8);
|
||||
ret |= LCD_WRITE_DATA(y1 & 0xff);
|
||||
|
||||
ret |= LCD_WRITE_CMD(LCD_RAMWR);
|
||||
LCD_CHECK(ESP_OK == ret, "Set window failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t lcd_st7796_write_ram_data(uint16_t color)
|
||||
{
|
||||
static uint8_t data[2];
|
||||
data[0] = (uint8_t)(color & 0xff);
|
||||
data[1] = (uint8_t)(color >> 8);
|
||||
return LCD_WRITE(data, 2);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_set_invert(bool is_invert)
|
||||
{
|
||||
return LCD_WRITE_CMD(is_invert ? LCD_INVON : LCD_INVOFF);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = lcd_st7796_set_window(x, y, x, y);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return lcd_st7796_write_ram_data(color);
|
||||
}
|
||||
|
||||
esp_err_t lcd_st7796_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
{
|
||||
esp_err_t ret;
|
||||
LCD_CHECK(NULL != bitmap, "bitmap pointer invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
LCD_IFACE_ACQUIRE();
|
||||
ret = lcd_st7796_set_window(x, y, x + w - 1, y + h - 1);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t len = w * h;
|
||||
ret = LCD_WRITE((uint8_t *)bitmap, 2 * len);
|
||||
LCD_IFACE_RELEASE();
|
||||
LCD_CHECK(ESP_OK == ret, "lcd write ram data failed", ESP_FAIL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t lcd_st7796_reg_config(void)
|
||||
{
|
||||
LCD_WRITE_CMD(0x11); //Sleep Out
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
LCD_WRITE_CMD(0xf0);
|
||||
LCD_WRITE_DATA(0xc3); //enable command 2 part 1
|
||||
LCD_WRITE_CMD(0xf0);
|
||||
LCD_WRITE_DATA(0x96); //enable command 2 part 2
|
||||
LCD_WRITE_CMD(0x36); //内存数据访问控制
|
||||
|
||||
LCD_WRITE_DATA(0x28);
|
||||
|
||||
LCD_WRITE_CMD(0x3a); //16bit pixel
|
||||
LCD_WRITE_DATA(0x55);
|
||||
|
||||
LCD_WRITE_CMD(0xb4);
|
||||
LCD_WRITE_DATA(0x01);
|
||||
|
||||
LCD_WRITE_CMD(0xb7); LCD_WRITE_DATA(0xc6);
|
||||
|
||||
LCD_WRITE_CMD(0xe8); LCD_WRITE_DATA(0x40);
|
||||
LCD_WRITE_DATA(0x8a); LCD_WRITE_DATA(0x00);
|
||||
LCD_WRITE_DATA(0x00); LCD_WRITE_DATA(0x29);
|
||||
LCD_WRITE_DATA(0x19); LCD_WRITE_DATA(0xa5);
|
||||
LCD_WRITE_DATA(0x33);
|
||||
|
||||
LCD_WRITE_CMD(0xc1); LCD_WRITE_DATA(0x06);
|
||||
LCD_WRITE_CMD(0xc2); LCD_WRITE_DATA(0xa7);
|
||||
LCD_WRITE_CMD(0xc5); LCD_WRITE_DATA(0x18);
|
||||
|
||||
LCD_WRITE_CMD(0xe0); LCD_WRITE_DATA(0xf0);
|
||||
LCD_WRITE_DATA(0x09); LCD_WRITE_DATA(0x0b);
|
||||
LCD_WRITE_DATA(0x06); LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x15); LCD_WRITE_DATA(0x2f);
|
||||
LCD_WRITE_DATA(0x54); LCD_WRITE_DATA(0x42);
|
||||
LCD_WRITE_DATA(0x3c); LCD_WRITE_DATA(0x17);
|
||||
LCD_WRITE_DATA(0x14); LCD_WRITE_DATA(0x18);
|
||||
LCD_WRITE_DATA(0x1b);
|
||||
|
||||
//Negative Voltage Gamma Coltrol
|
||||
LCD_WRITE_CMD(0xe1); LCD_WRITE_DATA(0xf0);
|
||||
LCD_WRITE_DATA(0x09); LCD_WRITE_DATA(0x0b);
|
||||
LCD_WRITE_DATA(0x06); LCD_WRITE_DATA(0x04);
|
||||
LCD_WRITE_DATA(0x03); LCD_WRITE_DATA(0x2d);
|
||||
LCD_WRITE_DATA(0x43); LCD_WRITE_DATA(0x42);
|
||||
LCD_WRITE_DATA(0x3b); LCD_WRITE_DATA(0x16);
|
||||
LCD_WRITE_DATA(0x14); LCD_WRITE_DATA(0x17);
|
||||
LCD_WRITE_DATA(0x1b);
|
||||
|
||||
LCD_WRITE_CMD(0xf0); LCD_WRITE_DATA(0x3c);
|
||||
LCD_WRITE_CMD(0xf0); LCD_WRITE_DATA(0x69);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
LCD_WRITE_CMD(0x29); //Display ON
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
#ifndef _IOT_LCD_ST7796_H_
|
||||
#define _IOT_LCD_ST7796_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t lcd_st7796_init(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_set_rotation(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_get_info(scr_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Set screen color invert
|
||||
*
|
||||
* @param is_invert true: color invert on, false: color invert off
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_set_invert(bool is_invert);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_write_ram_data(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t lcd_st7796_draw_bitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
// 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 "screen/interface_driver/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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// 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.
|
||||
|
||||
#ifndef _IOT_SCREEN_INTERFACE_DRIVER_H_
|
||||
#define _IOT_SCREEN_INTERFACE_DRIVER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "bus/include/i2s_lcd_driver.h"
|
||||
#include "bus/include/i2c_bus.h"
|
||||
#include "bus/include/spi_bus.h"
|
||||
|
||||
/**
|
||||
* @brief SPI interface configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
spi_bus_handle_t spi_bus; /*!< Handle of spi bus */
|
||||
int8_t pin_num_cs; /*!< SPI Chip Select Pin*/
|
||||
int8_t pin_num_dc; /*!< Pin to select Data or Command for LCD */
|
||||
int clk_freq; /*!< SPI clock frequency */
|
||||
bool swap_data; /*!< Whether to swap data */
|
||||
} scr_interface_spi_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2C interface configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
i2c_bus_handle_t i2c_bus; /*!< Handle of i2c bus */
|
||||
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
|
||||
uint16_t slave_addr; /*!< I2C slave address */
|
||||
} scr_interface_i2c_config_t;
|
||||
|
||||
/**
|
||||
* @brief Type of screen interface
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SCREEN_IFACE_I2C, /*!< I2C interface */
|
||||
SCREEN_IFACE_8080, /*!< 8080 parallel interface */
|
||||
SCREEN_IFACE_SPI, /*!< SPI interface */
|
||||
} scr_interface_type_t;
|
||||
|
||||
/**
|
||||
* @brief Define common function for screen interface driver
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
scr_interface_type_t type; /*!< Interface bus type, see scr_interface_type_t struct */
|
||||
esp_err_t (*write_cmd)(void *handle, uint16_t cmd); /*!< Function to write a command */
|
||||
esp_err_t (*write_data)(void *handle, uint16_t data); /*!< Function to write a data */
|
||||
esp_err_t (*write)(void *handle, const uint8_t *data, uint32_t length); /*!< Function to write a block data */
|
||||
esp_err_t (*read)(void *handle, uint8_t *data, uint32_t length); /*!< Function to read a block data */
|
||||
esp_err_t (*bus_acquire)(void *handle); /*!< Function to acquire interface bus */
|
||||
esp_err_t (*bus_release)(void *handle); /*!< Function to release interface bus */
|
||||
} scr_interface_driver_t;
|
||||
|
||||
/**
|
||||
* @brief Create screen interface driver
|
||||
*
|
||||
* @param type Type of screen interface
|
||||
* @param config configuration of interface driver
|
||||
* @param out_driver Pointer to a screen interface driver
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
* - ESP_FAIL Initialize failed
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory.
|
||||
*/
|
||||
esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_interface_driver_t **out_driver);
|
||||
|
||||
/**
|
||||
* @brief Delete screen interface driver
|
||||
*
|
||||
* @param driver screen interface driver to delete
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
*/
|
||||
esp_err_t scr_interface_delete(const scr_interface_driver_t *driver);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9341 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9486 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9488 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9806 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_NT35510 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_RM68120 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1306 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1307 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1322 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1351 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1963 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7789 y
|
||||
#define CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7796 y
|
||||
// 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 "screen/screen_driver.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "screen driver";
|
||||
|
||||
#define LCD_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Define screen instance
|
||||
*/
|
||||
/**< Colorful screen */
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9341
|
||||
extern scr_driver_t lcd_ili9341_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9486
|
||||
extern scr_driver_t lcd_ili9486_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9806
|
||||
extern scr_driver_t lcd_ili9806_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9488
|
||||
extern scr_driver_t lcd_ili9488_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_NT35510
|
||||
extern scr_driver_t lcd_nt35510_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_RM68120
|
||||
extern scr_driver_t lcd_rm68120_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1351
|
||||
extern scr_driver_t lcd_ssd1351_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1963
|
||||
extern scr_driver_t lcd_ssd1963_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7789
|
||||
extern scr_driver_t lcd_st7789_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7796
|
||||
extern scr_driver_t lcd_st7796_default_driver;
|
||||
#endif
|
||||
|
||||
/**< Monochrome screen */
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1306
|
||||
extern scr_driver_t lcd_ssd1306_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1307
|
||||
extern scr_driver_t lcd_ssd1307_default_driver;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1322
|
||||
extern scr_driver_t lcd_ssd1322_default_driver;
|
||||
#endif
|
||||
|
||||
esp_err_t scr_find_driver(scr_controller_t controller, scr_driver_t *out_screen)
|
||||
{
|
||||
LCD_CHECK(NULL != out_screen, "Pointer of screen is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
switch (controller) {
|
||||
/**< Colorful screen */
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9341
|
||||
case SCREEN_CONTROLLER_ILI9341:
|
||||
*out_screen = lcd_ili9341_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9806
|
||||
case SCREEN_CONTROLLER_ILI9806:
|
||||
*out_screen = lcd_ili9806_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9486
|
||||
case SCREEN_CONTROLLER_ILI9486:
|
||||
*out_screen = lcd_ili9486_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ILI9488
|
||||
case SCREEN_CONTROLLER_ILI9488:
|
||||
*out_screen = lcd_ili9488_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_NT35510
|
||||
case SCREEN_CONTROLLER_NT35510:
|
||||
*out_screen = lcd_nt35510_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_RM68120
|
||||
case SCREEN_CONTROLLER_RM68120:
|
||||
*out_screen = lcd_rm68120_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7789
|
||||
case SCREEN_CONTROLLER_ST7789:
|
||||
*out_screen = lcd_st7789_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7796
|
||||
case SCREEN_CONTROLLER_ST7796:
|
||||
*out_screen = lcd_st7796_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1351
|
||||
case SCREEN_CONTROLLER_SSD1351:
|
||||
*out_screen = lcd_ssd1351_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1963
|
||||
case SCREEN_CONTROLLER_SSD1963:
|
||||
*out_screen = lcd_ssd1963_default_driver;
|
||||
break;
|
||||
#endif
|
||||
|
||||
/**< Monochrome screen */
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1306
|
||||
case SCREEN_CONTROLLER_SSD1306:
|
||||
*out_screen = lcd_ssd1306_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1307
|
||||
case SCREEN_CONTROLLER_SSD1307:
|
||||
*out_screen = lcd_ssd1307_default_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_SSD1322
|
||||
case SCREEN_CONTROLLER_SSD1322:
|
||||
*out_screen = lcd_ssd1322_default_driver;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ESP_LOGE(TAG, "Screen controller not supported or not enabled in menuconfig");
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// 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.
|
||||
|
||||
#ifndef _IOT_SCREEN_DRIVER_H_
|
||||
#define _IOT_SCREEN_DRIVER_H_
|
||||
|
||||
#include "screen/interface_driver/scr_interface_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define all screen direction
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
/* @---> X
|
||||
|
|
||||
Y
|
||||
*/
|
||||
SCR_DIR_LRTB, /**< From left to right then from top to bottom, this consider as the original direction of the screen */
|
||||
|
||||
/* Y
|
||||
|
|
||||
@---> X
|
||||
*/
|
||||
SCR_DIR_LRBT, /**< From left to right then from bottom to top */
|
||||
|
||||
/* X <---@
|
||||
|
|
||||
Y
|
||||
*/
|
||||
SCR_DIR_RLTB, /**< From right to left then from top to bottom */
|
||||
|
||||
/* Y
|
||||
|
|
||||
X <---@
|
||||
*/
|
||||
SCR_DIR_RLBT, /**< From right to left then from bottom to top */
|
||||
|
||||
/* @---> Y
|
||||
|
|
||||
X
|
||||
*/
|
||||
SCR_DIR_TBLR, /**< From top to bottom then from left to right */
|
||||
|
||||
/* X
|
||||
|
|
||||
@---> Y
|
||||
*/
|
||||
SCR_DIR_BTLR, /**< From bottom to top then from left to right */
|
||||
|
||||
/* Y <---@
|
||||
|
|
||||
X
|
||||
*/
|
||||
SCR_DIR_TBRL, /**< From top to bottom then from right to left */
|
||||
|
||||
/* X
|
||||
|
|
||||
Y <---@
|
||||
*/
|
||||
SCR_DIR_BTRL, /**< From bottom to top then from right to left */
|
||||
|
||||
SCR_DIR_MAX,
|
||||
|
||||
/* Another way to represent rotation with 3 bit*/
|
||||
SCR_MIRROR_X = 0x40, /**< Mirror X-axis */
|
||||
SCR_MIRROR_Y = 0x20, /**< Mirror Y-axis */
|
||||
SCR_SWAP_XY = 0x80, /**< Swap XY axis */
|
||||
} scr_dir_t;
|
||||
|
||||
/**
|
||||
* @brief The types of colors that can be displayed on the screen
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SCR_COLOR_TYPE_MONO, /**< The screen is monochrome */
|
||||
SCR_COLOR_TYPE_GRAY, /**< The screen is gray */
|
||||
SCR_COLOR_TYPE_RGB565, /**< The screen is colorful */
|
||||
} scr_color_type_t;
|
||||
|
||||
/**
|
||||
* @brief All supported screen controllers
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
/* color screen */
|
||||
SCREEN_CONTROLLER_ILI9341,
|
||||
SCREEN_CONTROLLER_ILI9806,
|
||||
SCREEN_CONTROLLER_ILI9486,
|
||||
SCREEN_CONTROLLER_ILI9488,
|
||||
SCREEN_CONTROLLER_NT35510,
|
||||
SCREEN_CONTROLLER_RM68120,
|
||||
SCREEN_CONTROLLER_ST7789,
|
||||
SCREEN_CONTROLLER_ST7796,
|
||||
SCREEN_CONTROLLER_SSD1351,
|
||||
SCREEN_CONTROLLER_SSD1963,
|
||||
|
||||
/* monochrome screen */
|
||||
SCREEN_CONTROLLER_SSD1306,
|
||||
SCREEN_CONTROLLER_SSD1307,
|
||||
SCREEN_CONTROLLER_SSD1322,
|
||||
|
||||
} scr_controller_t;
|
||||
|
||||
/**
|
||||
* @brief configuration of screen controller
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
scr_interface_driver_t *interface_drv; /*!< Interface driver for screen */
|
||||
int8_t pin_num_rst; /*!< Pin to hardreset LCD*/
|
||||
int8_t pin_num_bckl; /*!< Pin for control backlight */
|
||||
uint8_t rst_active_level; /*!< Reset pin active level */
|
||||
uint8_t bckl_active_level; /*!< Backlight active level */
|
||||
uint16_t width; /*!< Screen width */
|
||||
uint16_t height; /*!< Screen height */
|
||||
uint16_t offset_hor; /*!< Offset of horizontal */
|
||||
uint16_t offset_ver; /*!< Offset of vertical */
|
||||
scr_dir_t rotate; /*!< Screen rotate direction */
|
||||
} scr_controller_config_t;
|
||||
|
||||
/**
|
||||
* @brief Information of screen
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint16_t width; /*!< Current screen width, it may change when apply to rotate */
|
||||
uint16_t height; /*!< Current screen height, it may change when apply to rotate */
|
||||
scr_dir_t dir; /*!< Current screen direction */
|
||||
scr_color_type_t color_type; /*!< Color type of the screen, See scr_color_type_t struct */
|
||||
uint8_t bpp; /*!< Bits per pixel */
|
||||
const char *name; /*!< Name of the screen */
|
||||
} scr_info_t;
|
||||
|
||||
/**
|
||||
* @brief Define a screen common function
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* @brief Initialize screen
|
||||
*
|
||||
* @param lcd_conf Pointer to a structure with lcd config arguments. see struct scr_controller_config_t
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Driver not installed
|
||||
*/
|
||||
esp_err_t (*init)(const scr_controller_config_t *lcd_conf);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize screen
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Deinitialize failed
|
||||
* - ESP_ERR_NOT_SUPPORTED unsupported
|
||||
*/
|
||||
esp_err_t (*deinit)(void);
|
||||
|
||||
/**
|
||||
* @brief Set screen direction of rotation
|
||||
*
|
||||
* @param dir Pointer to a scr_dir_t structure.
|
||||
* You can set the direction in two ways, for example, set it to "SCR_DIR_LRBT" or "SCR_MIRROR_Y", They are the same, depending on which expression you want to use
|
||||
*
|
||||
* @note Not all screens support eight directions, it depends on the screen controller.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*set_direction)(scr_dir_t dir);
|
||||
|
||||
/**
|
||||
* @brief Set screen window
|
||||
*
|
||||
* @param x0 Starting point in X direction
|
||||
* @param y0 Starting point in Y direction
|
||||
* @param x1 End point in X direction
|
||||
* @param y1 End point in Y direction
|
||||
*
|
||||
* @note When the BPP of the screen controller is less than 8, the coordinate value is limited to a multiple of some number
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*set_window)(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
|
||||
/**
|
||||
* @brief Write a RAM data
|
||||
*
|
||||
* @param color New color of a pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*write_ram_data)(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Draw one pixel in screen with color
|
||||
*
|
||||
* @param x X co-ordinate of set orientation
|
||||
* @param y Y co-ordinate of set orientation
|
||||
* @param color New color of the pixel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*draw_pixel)(uint16_t x, uint16_t y, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Fill the pixels on LCD screen with bitmap
|
||||
*
|
||||
* @param x Starting point in X direction
|
||||
* @param y Starting point in Y direction
|
||||
* @param w width of image in bitmap array
|
||||
* @param h height of image in bitmap array
|
||||
* @param bitmap pointer to bitmap array
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*draw_bitmap)(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap);
|
||||
|
||||
/**
|
||||
* @brief Get screen information
|
||||
*
|
||||
* @param info Pointer to a scr_info_t structure.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL Failed
|
||||
*/
|
||||
esp_err_t (*get_info)(scr_info_t *info);
|
||||
} scr_driver_t;
|
||||
|
||||
/**
|
||||
* @brief Find a screen driver
|
||||
*
|
||||
* @param controller Screen controller to initialize
|
||||
* @param out_screen Pointer to a screen driver
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
* - ESP_ERR_NOT_FOUND Screen controller was not found.
|
||||
*/
|
||||
esp_err_t scr_find_driver(scr_controller_t controller, scr_driver_t *out_screen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
#ifndef _INTERFACE_DRV_DEF_H_
|
||||
#define _INTERFACE_DRV_DEF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**< Define the function of interface instance */
|
||||
#define LCD_WRITE_CMD(cmd) g_lcd_handle.interface_drv->write_cmd(g_lcd_handle.interface_drv, (cmd))
|
||||
#define LCD_WRITE_DATA(data) g_lcd_handle.interface_drv->write_data(g_lcd_handle.interface_drv, (data))
|
||||
#define LCD_WRITE(data, length) g_lcd_handle.interface_drv->write(g_lcd_handle.interface_drv, (data), (length))
|
||||
#define LCD_READ(data, length) g_lcd_handle.interface_drv->read(g_lcd_handle.interface_drv, (data), (length))
|
||||
#define LCD_IFACE_ACQUIRE() g_lcd_handle.interface_drv->bus_acquire(g_lcd_handle.interface_drv)
|
||||
#define LCD_IFACE_RELEASE() g_lcd_handle.interface_drv->bus_release(g_lcd_handle.interface_drv)
|
||||
|
||||
static inline esp_err_t LCD_WRITE_REG(uint16_t cmd, uint16_t data)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = LCD_WRITE_CMD(cmd);
|
||||
ret |= LCD_WRITE_DATA(data);
|
||||
if (ESP_OK != ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 "stdint.h"
|
||||
#include "esp_log.h"
|
||||
#include "screen/screen_utility/screen_utility.h"
|
||||
|
||||
static const char *TAG = "screen utility";
|
||||
|
||||
void scr_utility_apply_offset(const scr_handle_t *lcd_handle, uint16_t res_hor, uint16_t res_ver, uint16_t *x0, uint16_t *y0, uint16_t *x1, uint16_t *y1)
|
||||
{
|
||||
scr_dir_t dir = lcd_handle->dir;
|
||||
if (SCR_DIR_MAX < dir) {
|
||||
dir >>= 5;
|
||||
}
|
||||
uint16_t xoffset=0, yoffset=0;
|
||||
switch (dir) {
|
||||
case SCR_DIR_LRTB:
|
||||
xoffset = lcd_handle->offset_hor;
|
||||
yoffset = lcd_handle->offset_ver;
|
||||
break;
|
||||
case SCR_DIR_LRBT:
|
||||
xoffset = lcd_handle->offset_hor;
|
||||
yoffset = res_ver - lcd_handle->offset_ver - lcd_handle->original_height;
|
||||
break;
|
||||
case SCR_DIR_RLTB:
|
||||
xoffset += res_hor - lcd_handle->offset_hor - lcd_handle->original_width;
|
||||
yoffset += lcd_handle->offset_ver;
|
||||
break;
|
||||
case SCR_DIR_RLBT:
|
||||
xoffset = res_hor - lcd_handle->offset_hor - lcd_handle->original_width;
|
||||
yoffset = res_ver - lcd_handle->offset_ver - lcd_handle->original_height;
|
||||
break;
|
||||
|
||||
case SCR_DIR_TBLR:
|
||||
xoffset = lcd_handle->offset_ver;
|
||||
yoffset = lcd_handle->offset_hor;
|
||||
break;
|
||||
case SCR_DIR_BTLR:
|
||||
yoffset = lcd_handle->offset_hor;
|
||||
xoffset = res_ver - lcd_handle->offset_ver - lcd_handle->original_height;
|
||||
break;
|
||||
case SCR_DIR_TBRL:
|
||||
yoffset += res_hor - lcd_handle->offset_hor - lcd_handle->original_width;
|
||||
xoffset += lcd_handle->offset_ver;
|
||||
break;
|
||||
case SCR_DIR_BTRL:
|
||||
yoffset = res_hor - lcd_handle->offset_hor - lcd_handle->original_width;
|
||||
xoffset = res_ver - lcd_handle->offset_ver - lcd_handle->original_height;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
ESP_LOGD(TAG, "dir=%d, offset=(%d, %d)", dir, xoffset, yoffset);
|
||||
*x0 += xoffset;
|
||||
*x1 += xoffset;
|
||||
*y0 += yoffset;
|
||||
*y1 += yoffset;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
#ifndef _SCREEN_UTILITY_H_
|
||||
#define _SCREEN_UTILITY_H_
|
||||
|
||||
#include "screen/screen_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Declare screen parameters
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
scr_interface_driver_t *interface_drv;
|
||||
uint16_t original_width;
|
||||
uint16_t original_height;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t offset_hor;
|
||||
uint16_t offset_ver;
|
||||
scr_dir_t dir;
|
||||
} scr_handle_t;
|
||||
|
||||
void scr_utility_apply_offset(const scr_handle_t *lcd_handle, uint16_t res_hor, uint16_t res_ver, uint16_t *x0, uint16_t *y0, uint16_t *x1, uint16_t *y1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
// Compatibility shim for new location of interface definitions.
|
||||
|
||||
#ifndef TENSORFLOW_LITE_BUILTIN_OP_DATA_H_
|
||||
#define TENSORFLOW_LITE_BUILTIN_OP_DATA_H_
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
|
||||
#endif // TENSORFLOW_LITE_BUILTIN_OP_DATA_H_
|
||||
@@ -0,0 +1,189 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
#define TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
|
||||
// DO NOT EDIT MANUALLY: This file is automatically generated by
|
||||
// `schema/builtin_ops_header/generator.cc`.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// The enum for builtin operators.
|
||||
// Note: CUSTOM, DELEGATE, and PLACEHOLDER_FOR_GREATER_OP_CODES are 3 special
|
||||
// ops which are not real built-in ops.
|
||||
typedef enum {
|
||||
kTfLiteBuiltinAdd = 0,
|
||||
kTfLiteBuiltinAveragePool2d = 1,
|
||||
kTfLiteBuiltinConcatenation = 2,
|
||||
kTfLiteBuiltinConv2d = 3,
|
||||
kTfLiteBuiltinDepthwiseConv2d = 4,
|
||||
kTfLiteBuiltinDepthToSpace = 5,
|
||||
kTfLiteBuiltinDequantize = 6,
|
||||
kTfLiteBuiltinEmbeddingLookup = 7,
|
||||
kTfLiteBuiltinFloor = 8,
|
||||
kTfLiteBuiltinFullyConnected = 9,
|
||||
kTfLiteBuiltinHashtableLookup = 10,
|
||||
kTfLiteBuiltinL2Normalization = 11,
|
||||
kTfLiteBuiltinL2Pool2d = 12,
|
||||
kTfLiteBuiltinLocalResponseNormalization = 13,
|
||||
kTfLiteBuiltinLogistic = 14,
|
||||
kTfLiteBuiltinLshProjection = 15,
|
||||
kTfLiteBuiltinLstm = 16,
|
||||
kTfLiteBuiltinMaxPool2d = 17,
|
||||
kTfLiteBuiltinMul = 18,
|
||||
kTfLiteBuiltinRelu = 19,
|
||||
kTfLiteBuiltinReluN1To1 = 20,
|
||||
kTfLiteBuiltinRelu6 = 21,
|
||||
kTfLiteBuiltinReshape = 22,
|
||||
kTfLiteBuiltinResizeBilinear = 23,
|
||||
kTfLiteBuiltinRnn = 24,
|
||||
kTfLiteBuiltinSoftmax = 25,
|
||||
kTfLiteBuiltinSpaceToDepth = 26,
|
||||
kTfLiteBuiltinSvdf = 27,
|
||||
kTfLiteBuiltinTanh = 28,
|
||||
kTfLiteBuiltinConcatEmbeddings = 29,
|
||||
kTfLiteBuiltinSkipGram = 30,
|
||||
kTfLiteBuiltinCall = 31,
|
||||
kTfLiteBuiltinCustom = 32,
|
||||
kTfLiteBuiltinEmbeddingLookupSparse = 33,
|
||||
kTfLiteBuiltinPad = 34,
|
||||
kTfLiteBuiltinUnidirectionalSequenceRnn = 35,
|
||||
kTfLiteBuiltinGather = 36,
|
||||
kTfLiteBuiltinBatchToSpaceNd = 37,
|
||||
kTfLiteBuiltinSpaceToBatchNd = 38,
|
||||
kTfLiteBuiltinTranspose = 39,
|
||||
kTfLiteBuiltinMean = 40,
|
||||
kTfLiteBuiltinSub = 41,
|
||||
kTfLiteBuiltinDiv = 42,
|
||||
kTfLiteBuiltinSqueeze = 43,
|
||||
kTfLiteBuiltinUnidirectionalSequenceLstm = 44,
|
||||
kTfLiteBuiltinStridedSlice = 45,
|
||||
kTfLiteBuiltinBidirectionalSequenceRnn = 46,
|
||||
kTfLiteBuiltinExp = 47,
|
||||
kTfLiteBuiltinTopkV2 = 48,
|
||||
kTfLiteBuiltinSplit = 49,
|
||||
kTfLiteBuiltinLogSoftmax = 50,
|
||||
kTfLiteBuiltinDelegate = 51,
|
||||
kTfLiteBuiltinBidirectionalSequenceLstm = 52,
|
||||
kTfLiteBuiltinCast = 53,
|
||||
kTfLiteBuiltinPrelu = 54,
|
||||
kTfLiteBuiltinMaximum = 55,
|
||||
kTfLiteBuiltinArgMax = 56,
|
||||
kTfLiteBuiltinMinimum = 57,
|
||||
kTfLiteBuiltinLess = 58,
|
||||
kTfLiteBuiltinNeg = 59,
|
||||
kTfLiteBuiltinPadv2 = 60,
|
||||
kTfLiteBuiltinGreater = 61,
|
||||
kTfLiteBuiltinGreaterEqual = 62,
|
||||
kTfLiteBuiltinLessEqual = 63,
|
||||
kTfLiteBuiltinSelect = 64,
|
||||
kTfLiteBuiltinSlice = 65,
|
||||
kTfLiteBuiltinSin = 66,
|
||||
kTfLiteBuiltinTransposeConv = 67,
|
||||
kTfLiteBuiltinSparseToDense = 68,
|
||||
kTfLiteBuiltinTile = 69,
|
||||
kTfLiteBuiltinExpandDims = 70,
|
||||
kTfLiteBuiltinEqual = 71,
|
||||
kTfLiteBuiltinNotEqual = 72,
|
||||
kTfLiteBuiltinLog = 73,
|
||||
kTfLiteBuiltinSum = 74,
|
||||
kTfLiteBuiltinSqrt = 75,
|
||||
kTfLiteBuiltinRsqrt = 76,
|
||||
kTfLiteBuiltinShape = 77,
|
||||
kTfLiteBuiltinPow = 78,
|
||||
kTfLiteBuiltinArgMin = 79,
|
||||
kTfLiteBuiltinFakeQuant = 80,
|
||||
kTfLiteBuiltinReduceProd = 81,
|
||||
kTfLiteBuiltinReduceMax = 82,
|
||||
kTfLiteBuiltinPack = 83,
|
||||
kTfLiteBuiltinLogicalOr = 84,
|
||||
kTfLiteBuiltinOneHot = 85,
|
||||
kTfLiteBuiltinLogicalAnd = 86,
|
||||
kTfLiteBuiltinLogicalNot = 87,
|
||||
kTfLiteBuiltinUnpack = 88,
|
||||
kTfLiteBuiltinReduceMin = 89,
|
||||
kTfLiteBuiltinFloorDiv = 90,
|
||||
kTfLiteBuiltinReduceAny = 91,
|
||||
kTfLiteBuiltinSquare = 92,
|
||||
kTfLiteBuiltinZerosLike = 93,
|
||||
kTfLiteBuiltinFill = 94,
|
||||
kTfLiteBuiltinFloorMod = 95,
|
||||
kTfLiteBuiltinRange = 96,
|
||||
kTfLiteBuiltinResizeNearestNeighbor = 97,
|
||||
kTfLiteBuiltinLeakyRelu = 98,
|
||||
kTfLiteBuiltinSquaredDifference = 99,
|
||||
kTfLiteBuiltinMirrorPad = 100,
|
||||
kTfLiteBuiltinAbs = 101,
|
||||
kTfLiteBuiltinSplitV = 102,
|
||||
kTfLiteBuiltinUnique = 103,
|
||||
kTfLiteBuiltinCeil = 104,
|
||||
kTfLiteBuiltinReverseV2 = 105,
|
||||
kTfLiteBuiltinAddN = 106,
|
||||
kTfLiteBuiltinGatherNd = 107,
|
||||
kTfLiteBuiltinCos = 108,
|
||||
kTfLiteBuiltinWhere = 109,
|
||||
kTfLiteBuiltinRank = 110,
|
||||
kTfLiteBuiltinElu = 111,
|
||||
kTfLiteBuiltinReverseSequence = 112,
|
||||
kTfLiteBuiltinMatrixDiag = 113,
|
||||
kTfLiteBuiltinQuantize = 114,
|
||||
kTfLiteBuiltinMatrixSetDiag = 115,
|
||||
kTfLiteBuiltinRound = 116,
|
||||
kTfLiteBuiltinHardSwish = 117,
|
||||
kTfLiteBuiltinIf = 118,
|
||||
kTfLiteBuiltinWhile = 119,
|
||||
kTfLiteBuiltinNonMaxSuppressionV4 = 120,
|
||||
kTfLiteBuiltinNonMaxSuppressionV5 = 121,
|
||||
kTfLiteBuiltinScatterNd = 122,
|
||||
kTfLiteBuiltinSelectV2 = 123,
|
||||
kTfLiteBuiltinDensify = 124,
|
||||
kTfLiteBuiltinSegmentSum = 125,
|
||||
kTfLiteBuiltinBatchMatmul = 126,
|
||||
kTfLiteBuiltinPlaceholderForGreaterOpCodes = 127,
|
||||
kTfLiteBuiltinCumsum = 128,
|
||||
kTfLiteBuiltinCallOnce = 129,
|
||||
kTfLiteBuiltinBroadcastTo = 130,
|
||||
kTfLiteBuiltinRfft2d = 131,
|
||||
kTfLiteBuiltinConv3d = 132,
|
||||
kTfLiteBuiltinImag = 133,
|
||||
kTfLiteBuiltinReal = 134,
|
||||
kTfLiteBuiltinComplexAbs = 135,
|
||||
kTfLiteBuiltinHashtable = 136,
|
||||
kTfLiteBuiltinHashtableFind = 137,
|
||||
kTfLiteBuiltinHashtableImport = 138,
|
||||
kTfLiteBuiltinHashtableSize = 139,
|
||||
kTfLiteBuiltinReduceAll = 140,
|
||||
kTfLiteBuiltinConv3dTranspose = 141,
|
||||
kTfLiteBuiltinVarHandle = 142,
|
||||
kTfLiteBuiltinReadVariable = 143,
|
||||
kTfLiteBuiltinAssignVariable = 144,
|
||||
kTfLiteBuiltinBroadcastArgs = 145,
|
||||
kTfLiteBuiltinRandomStandardNormal = 146,
|
||||
kTfLiteBuiltinBucketize = 147,
|
||||
kTfLiteBuiltinRandomUniform = 148,
|
||||
kTfLiteBuiltinMultinomial = 149,
|
||||
kTfLiteBuiltinGelu = 150,
|
||||
kTfLiteBuiltinDynamicUpdateSlice = 151,
|
||||
kTfLiteBuiltinRelu0To1 = 152,
|
||||
kTfLiteBuiltinUnsortedSegmentProd = 153,
|
||||
} TfLiteBuiltinOperator;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif // TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
@@ -0,0 +1,528 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_
|
||||
#define TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// TfLiteReshapeParams can't have dynamic data so we fix the maximum possible
|
||||
// number of dimensions.
|
||||
#define TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT 8
|
||||
|
||||
// TODO(aselle): Consider using "if this then that" for testing.
|
||||
|
||||
// Useful placeholder to put in otherwise empty structs to avoid size warnings.
|
||||
typedef struct {
|
||||
char dummy;
|
||||
} EmptyStructPlaceholder;
|
||||
|
||||
// IMPORTANT: All new members of structs must be added at the end to ensure
|
||||
// backwards compatibility.
|
||||
|
||||
// Possible padding types (for convolutions)
|
||||
typedef enum {
|
||||
kTfLitePaddingUnknown = 0,
|
||||
kTfLitePaddingSame,
|
||||
kTfLitePaddingValid,
|
||||
} TfLitePadding;
|
||||
|
||||
typedef enum {
|
||||
kTfLiteMirrorPaddingUnknown = 0,
|
||||
kTfLiteMirrorPaddingReflect,
|
||||
kTfLiteMirrorPaddingSymmetric,
|
||||
} TfLiteMirrorPaddingMode;
|
||||
|
||||
// TODO(b/130259536): We should move this out of builtin_op_data.
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int width_offset;
|
||||
int height_offset;
|
||||
} TfLitePaddingValues;
|
||||
|
||||
typedef struct {
|
||||
TfLiteMirrorPaddingMode mode;
|
||||
} TfLiteMirrorPaddingParams;
|
||||
|
||||
// Possible fused activation functions.
|
||||
typedef enum {
|
||||
kTfLiteActNone = 0,
|
||||
kTfLiteActRelu,
|
||||
kTfLiteActReluN1To1, // min(max(-1, x), 1)
|
||||
kTfLiteActRelu6, // min(max(0, x), 6)
|
||||
kTfLiteActTanh,
|
||||
kTfLiteActSignBit,
|
||||
kTfLiteActSigmoid,
|
||||
} TfLiteFusedActivation;
|
||||
|
||||
typedef struct {
|
||||
// Parameters for CONV_2D version 1.
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
int stride_height;
|
||||
TfLiteFusedActivation activation;
|
||||
|
||||
// Parameters for CONV_2D version 2.
|
||||
// Note: Version 2 supports dilation values not equal to 1.
|
||||
int dilation_width_factor;
|
||||
int dilation_height_factor;
|
||||
} TfLiteConvParams;
|
||||
|
||||
typedef struct {
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
int stride_height;
|
||||
int stride_depth;
|
||||
int dilation_width_factor;
|
||||
int dilation_height_factor;
|
||||
int dilation_depth_factor;
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteConv3DParams;
|
||||
|
||||
typedef TfLiteConv3DParams TfLiteConv3DTransposeParams;
|
||||
|
||||
typedef struct {
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
int stride_height;
|
||||
int filter_width;
|
||||
int filter_height;
|
||||
TfLiteFusedActivation activation;
|
||||
struct {
|
||||
TfLitePaddingValues padding;
|
||||
} computed;
|
||||
} TfLitePoolParams;
|
||||
|
||||
typedef struct {
|
||||
// Parameters for DepthwiseConv version 1 or above.
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
int stride_height;
|
||||
// `depth_multiplier` is redundant. It's used by CPU kernels in
|
||||
// TensorFlow 2.0 or below, but ignored in versions above.
|
||||
//
|
||||
// The information can be deduced from the shape of input and the shape of
|
||||
// weights. Since the TFLiteConverter toolchain doesn't support partially
|
||||
// specified shapes, relying on `depth_multiplier` stops us from supporting
|
||||
// graphs with dynamic shape tensors.
|
||||
//
|
||||
// Note: Some of the delegates (e.g. NNAPI, GPU) are still relying on this
|
||||
// field.
|
||||
int depth_multiplier;
|
||||
TfLiteFusedActivation activation;
|
||||
// Parameters for DepthwiseConv version 2 or above.
|
||||
int dilation_width_factor;
|
||||
int dilation_height_factor;
|
||||
} TfLiteDepthwiseConvParams;
|
||||
|
||||
typedef struct {
|
||||
int rank;
|
||||
TfLiteFusedActivation activation;
|
||||
|
||||
// Parameter for SVDF version 4.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteSVDFParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
|
||||
// Parameter for RNN version 3.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteRNNParams;
|
||||
|
||||
typedef struct {
|
||||
bool time_major;
|
||||
TfLiteFusedActivation activation;
|
||||
|
||||
// Parameter for Sequence RNN version 3.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteSequenceRNNParams;
|
||||
|
||||
typedef struct {
|
||||
bool time_major;
|
||||
TfLiteFusedActivation activation;
|
||||
bool merge_outputs;
|
||||
|
||||
// Parameter for Bidirectional RNN verison 3.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteBidirectionalSequenceRNNParams;
|
||||
|
||||
typedef enum {
|
||||
kTfLiteFullyConnectedWeightsFormatDefault = 0,
|
||||
kTfLiteFullyConnectedWeightsFormatShuffled4x16Int8 = 1,
|
||||
} TfLiteFullyConnectedWeightsFormat;
|
||||
|
||||
typedef struct {
|
||||
// Parameters for FullyConnected version 1 or above.
|
||||
TfLiteFusedActivation activation;
|
||||
|
||||
// Parameters for FullyConnected version 2 or above.
|
||||
TfLiteFullyConnectedWeightsFormat weights_format;
|
||||
|
||||
// Parameters for FullyConnected version 5 or above.
|
||||
// If set to true, then the number of dimensions in the input and the output
|
||||
// tensors are the same. Furthermore, all but the last dimension of the input
|
||||
// and output shapes will be equal.
|
||||
bool keep_num_dims;
|
||||
|
||||
// Parameters for FullyConnected version 7 or above.
|
||||
// If set to true and the weights are quantized, then non constant inputs
|
||||
// are quantized at evaluation time with asymmetric quantization.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteFullyConnectedParams;
|
||||
|
||||
typedef enum {
|
||||
kTfLiteLshProjectionUnknown = 0,
|
||||
kTfLiteLshProjectionSparse = 1,
|
||||
kTfLiteLshProjectionDense = 2,
|
||||
} TfLiteLSHProjectionType;
|
||||
|
||||
typedef struct {
|
||||
TfLiteLSHProjectionType type;
|
||||
} TfLiteLSHProjectionParams;
|
||||
|
||||
typedef struct {
|
||||
float beta;
|
||||
} TfLiteSoftmaxParams;
|
||||
|
||||
typedef struct {
|
||||
int axis;
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteConcatenationParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
// Parameter added for the version 4.
|
||||
bool pot_scale_int16;
|
||||
} TfLiteAddParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteSpaceToBatchNDParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteBatchToSpaceNDParams;
|
||||
|
||||
typedef struct {
|
||||
bool adj_x;
|
||||
bool adj_y;
|
||||
// Parameters for BatchMatMul version 4 or above.
|
||||
// If set to true and the weights are quantized, then non constant inputs
|
||||
// are quantized at evaluation time with asymmetric quantization.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteBatchMatMulParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteMulParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
// Parameter added for the version 5.
|
||||
bool pot_scale_int16;
|
||||
} TfLiteSubParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteDivParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteL2NormParams;
|
||||
|
||||
typedef struct {
|
||||
int radius;
|
||||
float bias;
|
||||
float alpha;
|
||||
float beta;
|
||||
} TfLiteLocalResponseNormParams;
|
||||
|
||||
typedef enum {
|
||||
kTfLiteLSTMFullKernel = 0,
|
||||
kTfLiteLSTMBasicKernel
|
||||
} TfLiteLSTMKernelType;
|
||||
|
||||
typedef struct {
|
||||
// Parameters for LSTM version 1.
|
||||
TfLiteFusedActivation activation;
|
||||
float cell_clip;
|
||||
float proj_clip;
|
||||
|
||||
// Parameters for LSTM version 2.
|
||||
// kTfLiteLSTMBasicKernel is only supported in version 2 or above.
|
||||
TfLiteLSTMKernelType kernel_type;
|
||||
|
||||
// Parameters for LSTM version 4.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteLSTMParams;
|
||||
|
||||
typedef struct {
|
||||
// Parameters needed for the underlying LSTM.
|
||||
TfLiteFusedActivation activation;
|
||||
float cell_clip;
|
||||
float proj_clip;
|
||||
|
||||
// If set to true then the first dimension is time, otherwise batch.
|
||||
bool time_major;
|
||||
|
||||
// Parameter for unidirectional sequence RNN version 3.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteUnidirectionalSequenceLSTMParams;
|
||||
|
||||
typedef struct {
|
||||
// Parameters supported by version 1:
|
||||
// Parameters inherited for the LSTM kernel.
|
||||
TfLiteFusedActivation activation;
|
||||
float cell_clip;
|
||||
float proj_clip;
|
||||
|
||||
// If true, store the outputs of both directions in the first output.
|
||||
bool merge_outputs;
|
||||
|
||||
// Parameters supported by version 2:
|
||||
// If set to true then the first dimension is time, otherwise batch.
|
||||
bool time_major;
|
||||
|
||||
// Parameters supported by version 4:
|
||||
// If set to true, then hybrid ops use asymmetric quantization for inputs.
|
||||
bool asymmetric_quantize_inputs;
|
||||
} TfLiteBidirectionalSequenceLSTMParams;
|
||||
|
||||
typedef struct {
|
||||
bool align_corners;
|
||||
// half_pixel_centers assumes pixels are of half the actual dimensions, and
|
||||
// yields more accurate resizes. Corresponds to the same argument for the
|
||||
// original TensorFlow op in TF2.0.
|
||||
bool half_pixel_centers;
|
||||
} TfLiteResizeBilinearParams;
|
||||
|
||||
typedef struct {
|
||||
bool align_corners;
|
||||
bool half_pixel_centers;
|
||||
} TfLiteResizeNearestNeighborParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLitePadParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLitePadV2Params;
|
||||
|
||||
typedef struct {
|
||||
// These fields are only used in old models for backward compatibility.
|
||||
// In the current implementation, we use the 2nd input of the op as the shape,
|
||||
// and these fields are unused.
|
||||
int shape[TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT];
|
||||
int num_dimensions;
|
||||
} TfLiteReshapeParams;
|
||||
|
||||
typedef struct {
|
||||
int ngram_size;
|
||||
int max_skip_size;
|
||||
bool include_all_ngrams;
|
||||
} TfLiteSkipGramParams;
|
||||
|
||||
typedef struct {
|
||||
int block_size;
|
||||
} TfLiteSpaceToDepthParams;
|
||||
|
||||
typedef struct {
|
||||
int block_size;
|
||||
} TfLiteDepthToSpaceParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteType in_data_type;
|
||||
TfLiteType out_data_type;
|
||||
} TfLiteCastParams;
|
||||
|
||||
typedef enum {
|
||||
kTfLiteCombinerTypeSum = 0,
|
||||
kTfLiteCombinerTypeMean = 1,
|
||||
kTfLiteCombinerTypeSqrtn = 2,
|
||||
} TfLiteCombinerType;
|
||||
|
||||
typedef struct {
|
||||
TfLiteCombinerType combiner;
|
||||
} TfLiteEmbeddingLookupSparseParams;
|
||||
|
||||
typedef struct {
|
||||
int axis;
|
||||
int batch_dims;
|
||||
} TfLiteGatherParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteTransposeParams;
|
||||
|
||||
typedef struct {
|
||||
bool keep_dims;
|
||||
} TfLiteReducerParams;
|
||||
|
||||
typedef struct {
|
||||
int num_splits;
|
||||
} TfLiteSplitParams;
|
||||
|
||||
typedef struct {
|
||||
int num_splits;
|
||||
} TfLiteSplitVParams;
|
||||
|
||||
typedef struct {
|
||||
// TODO(ahentz): We can't have dynamic data in this struct, at least not yet.
|
||||
// For now we will fix the maximum possible number of dimensions.
|
||||
int squeeze_dims[8];
|
||||
int num_squeeze_dims;
|
||||
} TfLiteSqueezeParams;
|
||||
|
||||
typedef struct {
|
||||
int begin_mask;
|
||||
int end_mask;
|
||||
int ellipsis_mask;
|
||||
int new_axis_mask;
|
||||
int shrink_axis_mask;
|
||||
} TfLiteStridedSliceParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteType output_type;
|
||||
} TfLiteArgMaxParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteType output_type;
|
||||
} TfLiteArgMinParams;
|
||||
|
||||
typedef struct {
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
int stride_height;
|
||||
} TfLiteTransposeConvParams;
|
||||
|
||||
typedef struct {
|
||||
bool validate_indices;
|
||||
} TfLiteSparseToDenseParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteType out_type;
|
||||
} TfLiteShapeParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteRankParams;
|
||||
|
||||
typedef struct {
|
||||
// Parameters supported by version 1:
|
||||
float min;
|
||||
float max;
|
||||
int num_bits;
|
||||
|
||||
// Parameters supported by version 2:
|
||||
bool narrow_range;
|
||||
} TfLiteFakeQuantParams;
|
||||
|
||||
typedef struct {
|
||||
int values_count;
|
||||
int axis;
|
||||
} TfLitePackParams;
|
||||
|
||||
typedef struct {
|
||||
int axis;
|
||||
} TfLiteOneHotParams;
|
||||
|
||||
typedef struct {
|
||||
int num;
|
||||
int axis;
|
||||
} TfLiteUnpackParams;
|
||||
|
||||
typedef struct {
|
||||
float alpha;
|
||||
} TfLiteLeakyReluParams;
|
||||
|
||||
typedef struct {
|
||||
TfLiteType index_out_type;
|
||||
} TfLiteUniqueParams;
|
||||
|
||||
typedef struct {
|
||||
int seq_dim;
|
||||
int batch_dim;
|
||||
} TfLiteReverseSequenceParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteMatrixDiagParams;
|
||||
|
||||
typedef struct {
|
||||
EmptyStructPlaceholder placeholder;
|
||||
} TfLiteMatrixSetDiagParams;
|
||||
|
||||
typedef struct {
|
||||
int then_subgraph_index;
|
||||
int else_subgraph_index;
|
||||
} TfLiteIfParams;
|
||||
|
||||
typedef struct {
|
||||
int cond_subgraph_index;
|
||||
int body_subgraph_index;
|
||||
} TfLiteWhileParams;
|
||||
|
||||
typedef struct {
|
||||
bool exclusive;
|
||||
bool reverse;
|
||||
} TfLiteCumsumParams;
|
||||
|
||||
typedef struct {
|
||||
int init_subgraph_index;
|
||||
} TfLiteCallOnceParams;
|
||||
|
||||
typedef struct {
|
||||
int table_id;
|
||||
TfLiteType key_dtype;
|
||||
TfLiteType value_dtype;
|
||||
} TfLiteHashtableParams;
|
||||
|
||||
typedef struct {
|
||||
const char* container;
|
||||
const char* shared_name;
|
||||
} TfLiteVarHandleParams;
|
||||
|
||||
typedef struct {
|
||||
int seed;
|
||||
int seed2;
|
||||
} TfLiteRandomParams;
|
||||
|
||||
typedef struct {
|
||||
int num_boundaries;
|
||||
// This points to the memory stored in the model (flatbuffer),
|
||||
// and is not owned.
|
||||
const float* boundaries;
|
||||
} TfLiteBucketizeParams;
|
||||
|
||||
typedef struct {
|
||||
bool approximate;
|
||||
} TfLiteGeluParams;
|
||||
|
||||
typedef struct {
|
||||
int num_segments;
|
||||
} TfLiteUnsortedSegmentProdParams;
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // TENSORFLOW_LITE_C_BUILTIN_OP_DATA_H_
|
||||
@@ -0,0 +1,130 @@
|
||||
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
// This file declares types used by the pure C inference API defined in c_api.h,
|
||||
// some of which are also used in the C++ and C kernel and interpreter APIs.
|
||||
|
||||
#ifndef TENSORFLOW_LITE_C_C_API_TYPES_H_
|
||||
#define TENSORFLOW_LITE_C_C_API_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Define TFL_CAPI_EXPORT macro to export a function properly with a shared
|
||||
// library.
|
||||
#ifdef SWIG
|
||||
#define TFL_CAPI_EXPORT
|
||||
#elif defined(TFL_STATIC_LIBRARY_BUILD)
|
||||
#define TFL_CAPI_EXPORT
|
||||
#else // not definded TFL_STATIC_LIBRARY_BUILD
|
||||
#if defined(_WIN32)
|
||||
#ifdef TFL_COMPILE_LIBRARY
|
||||
#define TFL_CAPI_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define TFL_CAPI_EXPORT __declspec(dllimport)
|
||||
#endif // TFL_COMPILE_LIBRARY
|
||||
#else
|
||||
#define TFL_CAPI_EXPORT __attribute__((visibility("default")))
|
||||
#endif // _WIN32
|
||||
#endif // SWIG
|
||||
|
||||
// Note that new error status values may be added in future in order to
|
||||
// indicate more fine-grained internal states, therefore, applications should
|
||||
// not rely on status values being members of the enum.
|
||||
typedef enum TfLiteStatus {
|
||||
kTfLiteOk = 0,
|
||||
|
||||
// Generally referring to an error in the runtime (i.e. interpreter)
|
||||
kTfLiteError = 1,
|
||||
|
||||
// Generally referring to an error from a TfLiteDelegate itself.
|
||||
kTfLiteDelegateError = 2,
|
||||
|
||||
// Generally referring to an error in applying a delegate due to
|
||||
// incompatibility between runtime and delegate, e.g., this error is returned
|
||||
// when trying to apply a TF Lite delegate onto a model graph that's already
|
||||
// immutable.
|
||||
kTfLiteApplicationError = 3,
|
||||
|
||||
// Generally referring to serialized delegate data not being found.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataNotFound = 4,
|
||||
|
||||
// Generally referring to data-writing issues in delegate serialization.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataWriteError = 5,
|
||||
|
||||
// Generally referring to data-reading issues in delegate serialization.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataReadError = 6,
|
||||
|
||||
// Generally referring to issues when the TF Lite model has ops that cannot be
|
||||
// resolved at runtime. This could happen when the specific op is not
|
||||
// registered or built with the TF Lite framework.
|
||||
kTfLiteUnresolvedOps = 7,
|
||||
} TfLiteStatus;
|
||||
|
||||
// Types supported by tensor
|
||||
typedef enum {
|
||||
kTfLiteNoType = 0,
|
||||
kTfLiteFloat32 = 1,
|
||||
kTfLiteInt32 = 2,
|
||||
kTfLiteUInt8 = 3,
|
||||
kTfLiteInt64 = 4,
|
||||
kTfLiteString = 5,
|
||||
kTfLiteBool = 6,
|
||||
kTfLiteInt16 = 7,
|
||||
kTfLiteComplex64 = 8,
|
||||
kTfLiteInt8 = 9,
|
||||
kTfLiteFloat16 = 10,
|
||||
kTfLiteFloat64 = 11,
|
||||
kTfLiteComplex128 = 12,
|
||||
kTfLiteUInt64 = 13,
|
||||
kTfLiteResource = 14,
|
||||
kTfLiteVariant = 15,
|
||||
kTfLiteUInt32 = 16,
|
||||
kTfLiteUInt16 = 17,
|
||||
} TfLiteType;
|
||||
|
||||
// Legacy. Will be deprecated in favor of TfLiteAffineQuantization.
|
||||
// If per-layer quantization is specified this field will still be populated in
|
||||
// addition to TfLiteAffineQuantization.
|
||||
// Parameters for asymmetric quantization. Quantized values can be converted
|
||||
// back to float using:
|
||||
// real_value = scale * (quantized_value - zero_point)
|
||||
typedef struct TfLiteQuantizationParams {
|
||||
float scale;
|
||||
int32_t zero_point;
|
||||
} TfLiteQuantizationParams;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Opaque types used by c_api.h, c_api_opaque.h and common.h.
|
||||
|
||||
// TfLiteOpaqueContext is an opaque version of TfLiteContext;
|
||||
typedef struct TfLiteOpaqueContext TfLiteOpaqueContext;
|
||||
|
||||
// TfLiteOpaqueNode is an opaque version of TfLiteNode;
|
||||
typedef struct TfLiteOpaqueNode TfLiteOpaqueNode;
|
||||
|
||||
// TfLiteOpaqueTensor is an opaque version of TfLiteTensor;
|
||||
typedef struct TfLiteOpaqueTensor TfLiteOpaqueTensor;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern C
|
||||
#endif
|
||||
#endif // TENSORFLOW_LITE_C_C_API_TYPES_H_
|
||||
@@ -0,0 +1,300 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/c/common.h"
|
||||
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
#include <string>
|
||||
|
||||
#include "tensorflow/lite/core/macros.h"
|
||||
#include "tensorflow/lite/tensorflow_profiler_logger.h"
|
||||
#endif
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
namespace tflite {
|
||||
// Use weak symbols here (even though they are guarded by macros) to avoid
|
||||
// build breakage when building a benchmark requires TFLite runs. The main
|
||||
// benchmark library should have tensor_profiler_logger dependency.
|
||||
TFLITE_ATTRIBUTE_WEAK void OnTfLiteTensorAlloc(TfLiteTensor* tensor,
|
||||
size_t num_bytes);
|
||||
|
||||
TFLITE_ATTRIBUTE_WEAK void OnTfLiteTensorDealloc(TfLiteTensor* tensor);
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TF_LITE_TENSORFLOW_PROFILER
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t TfLiteIntArrayGetSizeInBytes(int size) {
|
||||
static TfLiteIntArray dummy;
|
||||
|
||||
size_t computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_size;
|
||||
}
|
||||
|
||||
int TfLiteIntArrayEqual(const TfLiteIntArray* a, const TfLiteIntArray* b) {
|
||||
if (a == b) return 1;
|
||||
if (a == nullptr || b == nullptr) return 0;
|
||||
return TfLiteIntArrayEqualsArray(a, b->size, b->data);
|
||||
}
|
||||
|
||||
int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size,
|
||||
const int b_data[]) {
|
||||
if (a == nullptr) return (b_size == 0);
|
||||
if (a->size != b_size) return 0;
|
||||
int i = 0;
|
||||
for (; i < a->size; i++)
|
||||
if (a->data[i] != b_data[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
|
||||
TfLiteIntArray* TfLiteIntArrayCreate(int size) {
|
||||
size_t alloc_size = TfLiteIntArrayGetSizeInBytes(size);
|
||||
if (alloc_size <= 0) return nullptr;
|
||||
TfLiteIntArray* ret = (TfLiteIntArray*)malloc(alloc_size);
|
||||
if (!ret) return ret;
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TfLiteIntArray* TfLiteIntArrayCopy(const TfLiteIntArray* src) {
|
||||
if (!src) return nullptr;
|
||||
TfLiteIntArray* ret = TfLiteIntArrayCreate(src->size);
|
||||
if (ret) {
|
||||
memcpy(ret->data, src->data, src->size * sizeof(int));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TfLiteIntArrayFree(TfLiteIntArray* a) { free(a); }
|
||||
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
int TfLiteFloatArrayGetSizeInBytes(int size) {
|
||||
static TfLiteFloatArray dummy;
|
||||
|
||||
int computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_size;
|
||||
}
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
|
||||
TfLiteFloatArray* TfLiteFloatArrayCreate(int size) {
|
||||
TfLiteFloatArray* ret =
|
||||
(TfLiteFloatArray*)malloc(TfLiteFloatArrayGetSizeInBytes(size));
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TfLiteFloatArrayFree(TfLiteFloatArray* a) { free(a); }
|
||||
|
||||
void TfLiteTensorDataFree(TfLiteTensor* t) {
|
||||
if (t->allocation_type == kTfLiteDynamic ||
|
||||
t->allocation_type == kTfLitePersistentRo) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(t);
|
||||
#endif
|
||||
free(t->data.raw);
|
||||
}
|
||||
t->data.raw = nullptr;
|
||||
}
|
||||
|
||||
void TfLiteQuantizationFree(TfLiteQuantization* quantization) {
|
||||
if (quantization->type == kTfLiteAffineQuantization) {
|
||||
TfLiteAffineQuantization* q_params =
|
||||
(TfLiteAffineQuantization*)(quantization->params);
|
||||
if (q_params->scale) {
|
||||
TfLiteFloatArrayFree(q_params->scale);
|
||||
q_params->scale = nullptr;
|
||||
}
|
||||
if (q_params->zero_point) {
|
||||
TfLiteIntArrayFree(q_params->zero_point);
|
||||
q_params->zero_point = nullptr;
|
||||
}
|
||||
free(q_params);
|
||||
}
|
||||
quantization->params = nullptr;
|
||||
quantization->type = kTfLiteNoQuantization;
|
||||
}
|
||||
|
||||
void TfLiteSparsityFree(TfLiteSparsity* sparsity) {
|
||||
if (sparsity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sparsity->traversal_order) {
|
||||
TfLiteIntArrayFree(sparsity->traversal_order);
|
||||
sparsity->traversal_order = nullptr;
|
||||
}
|
||||
|
||||
if (sparsity->block_map) {
|
||||
TfLiteIntArrayFree(sparsity->block_map);
|
||||
sparsity->block_map = nullptr;
|
||||
}
|
||||
|
||||
if (sparsity->dim_metadata) {
|
||||
int i = 0;
|
||||
for (; i < sparsity->dim_metadata_size; i++) {
|
||||
TfLiteDimensionMetadata metadata = sparsity->dim_metadata[i];
|
||||
if (metadata.format == kTfLiteDimSparseCSR) {
|
||||
TfLiteIntArrayFree(metadata.array_segments);
|
||||
metadata.array_segments = nullptr;
|
||||
TfLiteIntArrayFree(metadata.array_indices);
|
||||
metadata.array_indices = nullptr;
|
||||
}
|
||||
}
|
||||
free(sparsity->dim_metadata);
|
||||
sparsity->dim_metadata = nullptr;
|
||||
}
|
||||
|
||||
free(sparsity);
|
||||
}
|
||||
|
||||
void TfLiteTensorFree(TfLiteTensor* t) {
|
||||
TfLiteTensorDataFree(t);
|
||||
if (t->dims) TfLiteIntArrayFree(t->dims);
|
||||
t->dims = nullptr;
|
||||
|
||||
if (t->dims_signature) {
|
||||
TfLiteIntArrayFree((TfLiteIntArray*)t->dims_signature);
|
||||
}
|
||||
t->dims_signature = nullptr;
|
||||
|
||||
TfLiteQuantizationFree(&t->quantization);
|
||||
TfLiteSparsityFree(t->sparsity);
|
||||
t->sparsity = nullptr;
|
||||
}
|
||||
|
||||
void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
|
||||
TfLiteQuantizationParams quantization, char* buffer,
|
||||
size_t size, TfLiteAllocationType allocation_type,
|
||||
const void* allocation, bool is_variable,
|
||||
TfLiteTensor* tensor) {
|
||||
TfLiteTensorFree(tensor);
|
||||
tensor->type = type;
|
||||
tensor->name = name;
|
||||
tensor->dims = dims;
|
||||
tensor->params = quantization;
|
||||
tensor->data.raw = buffer;
|
||||
tensor->bytes = size;
|
||||
tensor->allocation_type = allocation_type;
|
||||
tensor->allocation = allocation;
|
||||
tensor->is_variable = is_variable;
|
||||
|
||||
tensor->quantization.type = kTfLiteNoQuantization;
|
||||
tensor->quantization.params = nullptr;
|
||||
}
|
||||
|
||||
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) {
|
||||
if (!src || !dst) return kTfLiteOk;
|
||||
if (src->bytes != dst->bytes) return kTfLiteError;
|
||||
if (src == dst) return kTfLiteOk;
|
||||
|
||||
dst->type = src->type;
|
||||
if (dst->dims) TfLiteIntArrayFree(dst->dims);
|
||||
dst->dims = TfLiteIntArrayCopy(src->dims);
|
||||
memcpy(dst->data.raw, src->data.raw, src->bytes);
|
||||
dst->buffer_handle = src->buffer_handle;
|
||||
dst->data_is_stale = src->data_is_stale;
|
||||
dst->delegate = src->delegate;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
||||
if (tensor->allocation_type != kTfLiteDynamic &&
|
||||
tensor->allocation_type != kTfLitePersistentRo) {
|
||||
return;
|
||||
}
|
||||
// TODO(b/145340303): Tensor data should be aligned.
|
||||
if (!tensor->data.raw) {
|
||||
tensor->data.raw = (char*)malloc(num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
} else if (num_bytes > tensor->bytes) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(tensor);
|
||||
#endif
|
||||
tensor->data.raw = (char*)realloc(tensor->data.raw, num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
}
|
||||
tensor->bytes = num_bytes;
|
||||
}
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
const char* TfLiteTypeGetName(TfLiteType type) {
|
||||
switch (type) {
|
||||
case kTfLiteNoType:
|
||||
return "NOTYPE";
|
||||
case kTfLiteFloat32:
|
||||
return "FLOAT32";
|
||||
case kTfLiteUInt16:
|
||||
return "UINT16";
|
||||
case kTfLiteInt16:
|
||||
return "INT16";
|
||||
case kTfLiteInt32:
|
||||
return "INT32";
|
||||
case kTfLiteUInt32:
|
||||
return "UINT32";
|
||||
case kTfLiteUInt8:
|
||||
return "UINT8";
|
||||
case kTfLiteInt8:
|
||||
return "INT8";
|
||||
case kTfLiteInt64:
|
||||
return "INT64";
|
||||
case kTfLiteUInt64:
|
||||
return "UINT64";
|
||||
case kTfLiteBool:
|
||||
return "BOOL";
|
||||
case kTfLiteComplex64:
|
||||
return "COMPLEX64";
|
||||
case kTfLiteComplex128:
|
||||
return "COMPLEX128";
|
||||
case kTfLiteString:
|
||||
return "STRING";
|
||||
case kTfLiteFloat16:
|
||||
return "FLOAT16";
|
||||
case kTfLiteFloat64:
|
||||
return "FLOAT64";
|
||||
case kTfLiteResource:
|
||||
return "RESOURCE";
|
||||
case kTfLiteVariant:
|
||||
return "VARIANT";
|
||||
}
|
||||
return "Unknown type";
|
||||
}
|
||||
|
||||
TfLiteDelegate TfLiteDelegateCreate() { return TfLiteDelegate{}; }
|
||||
|
||||
} // extern "C"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
// This provides a few C++ helpers that are useful for manipulating C structures
|
||||
// in C++.
|
||||
#ifndef TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
#define TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Provide a range iterable wrapper for TfLiteIntArray* (C lists that TfLite
|
||||
// C api uses. Can't use the google array_view, since we can't depend on even
|
||||
// absl for embedded device reasons.
|
||||
class TfLiteIntArrayView {
|
||||
public:
|
||||
// Construct a view of a TfLiteIntArray*. Note, `int_array` should be non-null
|
||||
// and this view does not take ownership of it.
|
||||
explicit TfLiteIntArrayView(const TfLiteIntArray* int_array)
|
||||
: int_array_(int_array) {}
|
||||
|
||||
TfLiteIntArrayView(const TfLiteIntArrayView&) = default;
|
||||
TfLiteIntArrayView& operator=(const TfLiteIntArrayView& rhs) = default;
|
||||
|
||||
typedef const int* const_iterator;
|
||||
const_iterator begin() const { return int_array_->data; }
|
||||
const_iterator end() const { return &int_array_->data[int_array_->size]; }
|
||||
size_t size() const { return end() - begin(); }
|
||||
int operator[](size_t pos) const { return int_array_->data[pos]; }
|
||||
|
||||
private:
|
||||
const TfLiteIntArray* int_array_;
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
@@ -0,0 +1,38 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include <cstdarg>
|
||||
|
||||
namespace tflite {
|
||||
|
||||
int ErrorReporter::Report(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int code = Report(format, args);
|
||||
va_end(args);
|
||||
return code;
|
||||
}
|
||||
|
||||
// TODO(aselle): Make the name of ReportError on context the same, so
|
||||
// we can use the ensure functions w/o a context and w/ a reporter.
|
||||
int ErrorReporter::ReportError(void*, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int code = Report(format, args);
|
||||
va_end(args);
|
||||
return code;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
namespace tflite {
|
||||
|
||||
/// A functor that reports error to supporting system. Invoked similar to
|
||||
/// printf.
|
||||
///
|
||||
/// Usage:
|
||||
/// ErrorReporter foo;
|
||||
/// foo.Report("test %d", 5);
|
||||
/// or
|
||||
/// va_list args;
|
||||
/// foo.Report("test %d", args); // where args is va_list
|
||||
///
|
||||
/// Subclass ErrorReporter to provide another reporting destination.
|
||||
/// For example, if you have a GUI program, you might redirect to a buffer
|
||||
/// that drives a GUI error log box.
|
||||
class ErrorReporter {
|
||||
public:
|
||||
virtual ~ErrorReporter() {}
|
||||
virtual int Report(const char* format, va_list args) = 0;
|
||||
int Report(const char* format, ...);
|
||||
int ReportError(void*, const char* format, ...);
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
// You should not make bare calls to the error reporter, instead use the
|
||||
// TF_LITE_REPORT_ERROR macro, since this allows message strings to be
|
||||
// stripped when the binary size has to be optimized. If you are looking to
|
||||
// reduce binary size, define TF_LITE_STRIP_ERROR_STRINGS when compiling and
|
||||
// every call will be stubbed out, taking no memory.
|
||||
#ifndef TF_LITE_STRIP_ERROR_STRINGS
|
||||
#define TF_LITE_REPORT_ERROR(reporter, ...) \
|
||||
do { \
|
||||
static_cast<tflite::ErrorReporter*>(reporter)->Report(__VA_ARGS__); \
|
||||
} while (false)
|
||||
#else // TF_LITE_STRIP_ERROR_STRINGS
|
||||
#define TF_LITE_REPORT_ERROR(reporter, ...)
|
||||
#endif // TF_LITE_STRIP_ERROR_STRINGS
|
||||
|
||||
#endif // TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,403 @@
|
||||
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
|
||||
// These functions transform codes and data structures that are defined in the
|
||||
// flatbuffer serialization format into in-memory values that are used by the
|
||||
// runtime API and interpreter.
|
||||
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Interface class for builtin data allocations.
|
||||
class BuiltinDataAllocator {
|
||||
public:
|
||||
virtual void* Allocate(size_t size, size_t alignment_hint) = 0;
|
||||
virtual void Deallocate(void* data) = 0;
|
||||
|
||||
// Allocate a structure, but make sure it is a POD structure that doesn't
|
||||
// require constructors to run. The reason we do this, is that Interpreter's C
|
||||
// extension part will take ownership so destructors will not be run during
|
||||
// deallocation.
|
||||
template <typename T>
|
||||
T* AllocatePOD() {
|
||||
// TODO(b/154346074): Change this to is_trivially_destructible when all
|
||||
// platform targets support that properly.
|
||||
static_assert(std::is_pod<T>::value, "Builtin data structure must be POD.");
|
||||
void* allocated_memory = this->Allocate(sizeof(T), alignof(T));
|
||||
return new (allocated_memory) T();
|
||||
}
|
||||
|
||||
virtual ~BuiltinDataAllocator() {}
|
||||
};
|
||||
|
||||
// Parse the appropriate data out of the op.
|
||||
//
|
||||
// This handles builtin data explicitly as there are flatbuffer schemas.
|
||||
// If it returns kTfLiteOk, it passes the data out with `builtin_data`. The
|
||||
// calling function has to pass in an allocator object, and this allocator
|
||||
// will be called to reserve space for the output data. If the calling
|
||||
// function's allocator reserves memory on the heap, then it's the calling
|
||||
// function's responsibility to free it.
|
||||
// If it returns kTfLiteError, `builtin_data` will be `nullptr`.
|
||||
TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
// Converts the tensor data type used in the flat buffer to the representation
|
||||
// used by the runtime.
|
||||
TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType* type,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
TfLiteStatus ParseAbs(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAdd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAddN(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseArgMax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAssignVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBatchMatMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBatchToSpaceNd(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBroadcastArgs(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBroadcastTo(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCeil(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCast(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseConcatenation(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseConv2D(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCos(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCumsum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDepthToSpace(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDepthwiseConv2D(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDequantize(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDiv(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseElu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseExp(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseExpandDims(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFill(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloor(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloorDiv(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloorMod(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFullyConnected(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGather(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGatherNd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGreater(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGreaterEqual(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseHardSwish(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLeakyRelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLess(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLessEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLog(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalAnd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalNot(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalOr(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogistic(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLSTM(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMaximum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMinimum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseNeg(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseNotEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePack(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePadV2(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePool(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePow(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePrelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseQuantize(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReadVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRelu6(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReshape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseResizeBilinear(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseResizeNearestNeighbor(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRound(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRsqrt(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSlice(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSpaceToBatchNd(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSpaceToDepth(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSplit(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSplitV(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSqueeze(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSqrt(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSquare(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseStridedSlice(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSub(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSvdf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTanh(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTranspose(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTransposeConv(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseWhile(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseZerosLike(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/core/api/op_resolver.h"
|
||||
|
||||
#include "third_party/flatbuffers/flatbuffers.h" // from @flatbuffers
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
TfLiteStatus GetRegistrationFromOpCode(
|
||||
const OperatorCode* opcode, const OpResolver& op_resolver,
|
||||
ErrorReporter* error_reporter, const TfLiteRegistration** registration) {
|
||||
TfLiteStatus status = kTfLiteOk;
|
||||
*registration = nullptr;
|
||||
auto builtin_code = GetBuiltinCode(opcode);
|
||||
int version = opcode->version();
|
||||
|
||||
if (builtin_code > BuiltinOperator_MAX) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter,
|
||||
"Op builtin_code out of range: %d. Are you using old TFLite binary "
|
||||
"with newer model?",
|
||||
builtin_code);
|
||||
status = kTfLiteError;
|
||||
} else if (builtin_code != BuiltinOperator_CUSTOM) {
|
||||
*registration = op_resolver.FindOp(builtin_code, version);
|
||||
if (*registration == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter,
|
||||
"Didn't find op for builtin opcode '%s' version '%d'. "
|
||||
"An older version of this builtin might be supported. "
|
||||
"Are you using an old TFLite binary with a newer model?\n",
|
||||
EnumNameBuiltinOperator(builtin_code), version);
|
||||
status = kTfLiteError;
|
||||
}
|
||||
} else if (!opcode->custom_code()) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter,
|
||||
"Operator with CUSTOM builtin_code has no custom_code.\n");
|
||||
status = kTfLiteError;
|
||||
} else {
|
||||
const char* name = opcode->custom_code()->c_str();
|
||||
*registration = op_resolver.FindOp(name, version);
|
||||
if (*registration == nullptr) {
|
||||
// Do not report error for unresolved custom op, we do the final check
|
||||
// while preparing ops.
|
||||
status = kTfLiteError;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,140 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
// Opaque type similar to TfLiteDelegate / TfLiteOpaqueDelegate.
|
||||
// This is used for cases (e.g. when using "TF Lite with Google Play Services")
|
||||
// where the TF Lite runtime might be built using a newer (or older)
|
||||
// version of the TF Lite sources than the app, and hence might have a
|
||||
// different definition of the TfLiteDelegate type. TF Lite APIs use
|
||||
// TfLiteOpaqueDelegate rather than TfLiteDelegate when they want to
|
||||
// refer to a delegate defined with that potentially different version
|
||||
// of the TfLiteDelegate type.
|
||||
struct TfLiteOpaqueDelegateStruct;
|
||||
|
||||
namespace tflite {
|
||||
|
||||
/// Abstract interface that returns TfLiteRegistrations given op codes or custom
|
||||
/// op names. This is the mechanism that ops being referenced in the flatbuffer
|
||||
/// model are mapped to executable function pointers (TfLiteRegistrations).
|
||||
class OpResolver {
|
||||
public:
|
||||
/// Finds the op registration for a builtin operator by enum code.
|
||||
virtual const TfLiteRegistration* FindOp(tflite::BuiltinOperator op,
|
||||
int version) const = 0;
|
||||
/// Finds the op registration of a custom operator by op name.
|
||||
virtual const TfLiteRegistration* FindOp(const char* op,
|
||||
int version) const = 0;
|
||||
|
||||
// Represents a sequence of delegates.
|
||||
using TfLiteDelegatePtrVector =
|
||||
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>>;
|
||||
|
||||
// Returns optional delegates for resolving and handling ops in the flatbuffer
|
||||
// model. This may be used in addition to the standard TfLiteRegistration
|
||||
// lookup for graph resolution.
|
||||
// WARNING: This API is deprecated, GetDelegateCreators is preferred.
|
||||
virtual TfLiteDelegatePtrVector GetDelegates(int num_threads) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Represents a function that creates a TfLite delegate instance.
|
||||
using TfLiteDelegateCreator =
|
||||
std::function<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>(
|
||||
int /*num_threads*/)>;
|
||||
|
||||
// Represents a sequence of delegate creator functions.
|
||||
using TfLiteDelegateCreators = std::vector<TfLiteDelegateCreator>;
|
||||
|
||||
// Returns a vector of delegate creators to create optional delegates for
|
||||
// resolving and handling ops in the flatbuffer model. This may be used in
|
||||
// addition to the standard TfLiteRegistration lookup for graph resolution.
|
||||
//
|
||||
// Note that this method is not used (will not be called) if you are using
|
||||
// TF Lite in Google Play Services; the GetOpaqueDelegateCreators method
|
||||
// (see below) is used for that case.
|
||||
virtual TfLiteDelegateCreators GetDelegateCreators() const { return {}; }
|
||||
|
||||
// TODO(b/202712825): it would be nice if we could avoid the need for separate
|
||||
// "opaque" types & methods for use only with TF Lite in Google Play Services.
|
||||
|
||||
// Represents an opaque delegate instance.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegatePtr =
|
||||
std::unique_ptr<TfLiteOpaqueDelegateStruct,
|
||||
void (*)(TfLiteOpaqueDelegateStruct*)>;
|
||||
|
||||
// Represents a function that creates an opaque delegate instance.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegateCreator =
|
||||
std::function<TfLiteOpaqueDelegatePtr(int /*num_threads*/)>;
|
||||
|
||||
// Represents a sequence of opaque delegate creator functions.
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
using TfLiteOpaqueDelegateCreators = std::vector<TfLiteOpaqueDelegateCreator>;
|
||||
|
||||
// Returns a vector of opaque delegate creators to create optional opaque
|
||||
// delegates for resolving and handling ops in the flatbuffer model. This may
|
||||
// be used in addition to the standard TfLiteRegistration lookup for graph
|
||||
// resolution.
|
||||
//
|
||||
// Note that this method will be called only if you are using TF Lite in
|
||||
// Google Play Services; if you are using regular TF Lite, GetDelegateCreators
|
||||
// (see above) is used instead.
|
||||
//
|
||||
// WARNING: Experimental interface, subject to change.
|
||||
virtual TfLiteOpaqueDelegateCreators GetOpaqueDelegateCreators() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual ~OpResolver() {}
|
||||
|
||||
private:
|
||||
/// Returns true if this OpResolver may contain any "user defined" ops.
|
||||
/// By "user defined" ops, we mean any op definitions other than those
|
||||
/// contained in tflite::ops::builtin::BuiltinOpResolver.
|
||||
///
|
||||
/// If this method returns true, it doesn't necessarily mean that the
|
||||
/// OpResolver contains a user-defined op, just that the absence of
|
||||
/// user-defined ops can't be guaranteed.
|
||||
///
|
||||
/// Note that "user-defined" ops are not the same as "custom" ops;
|
||||
/// BuiltinOpResolver may support certain "custom" ops, in addition to
|
||||
/// "builtin" ops, and may not support all of the "builtin" op enum values.
|
||||
virtual bool MayContainUserDefinedOps() const { return true; }
|
||||
|
||||
friend class OpResolverInternal;
|
||||
};
|
||||
|
||||
// Handles the logic for converting between an OperatorCode structure extracted
|
||||
// from a flatbuffer and information about a registered operator
|
||||
// implementation.
|
||||
TfLiteStatus GetRegistrationFromOpCode(const OperatorCode* opcode,
|
||||
const OpResolver& op_resolver,
|
||||
ErrorReporter* error_reporter,
|
||||
const TfLiteRegistration** registration);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/core/api/tensor_utils.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
TfLiteStatus ResetVariableTensor(TfLiteTensor* tensor) {
|
||||
if (!tensor->is_variable) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
// TODO(b/115961645): Implement - If a variable tensor has a buffer, reset it
|
||||
// to the value of the buffer.
|
||||
int value = 0;
|
||||
if (tensor->type == kTfLiteInt8) {
|
||||
value = tensor->params.zero_point;
|
||||
}
|
||||
// TODO(b/139446230): Provide a platform header to better handle these
|
||||
// specific scenarios.
|
||||
#if __ANDROID__ || defined(__x86_64__) || defined(__i386__) || \
|
||||
defined(__i386) || defined(__x86__) || defined(__X86__) || \
|
||||
defined(_X86_) || defined(_M_IX86) || defined(_M_X64)
|
||||
memset(tensor->data.raw, value, tensor->bytes);
|
||||
#else
|
||||
char* raw_ptr = tensor->data.raw;
|
||||
for (size_t i = 0; i < tensor->bytes; ++i) {
|
||||
*raw_ptr = value;
|
||||
raw_ptr++;
|
||||
}
|
||||
#endif
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Resets a variable tensor to the default value.
|
||||
TfLiteStatus ResetVariableTensor(TfLiteTensor* tensor);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline int CountLeadingZeros32Slow(uint64_t n) {
|
||||
int zeroes = 28;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros32(uint32_t n) {
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return 31 - result;
|
||||
}
|
||||
return 32;
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// Handle 0 as a special case because __builtin_clz(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 32;
|
||||
}
|
||||
return __builtin_clz(n);
|
||||
#else
|
||||
return CountLeadingZeros32Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int MostSignificantBit32(uint32_t n) {
|
||||
return 32 - CountLeadingZeros32(n);
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros64Slow(uint64_t n) {
|
||||
int zeroes = 60;
|
||||
if (n >> 32) zeroes -= 32, n >>= 32;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros64(uint64_t n) {
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
// MSVC does not have __builtin_clzll. Use _BitScanReverse64.
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse64(&result, n)) {
|
||||
return 63 - result;
|
||||
}
|
||||
return 64;
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC does not have __builtin_clzll. Compose two calls to _BitScanReverse
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if ((n >> 32) && _BitScanReverse(&result, n >> 32)) {
|
||||
return 31 - result;
|
||||
}
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return 63 - result;
|
||||
}
|
||||
return 64;
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 64;
|
||||
}
|
||||
return __builtin_clzll(n);
|
||||
#else
|
||||
return CountLeadingZeros64Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int MostSignificantBit64(uint64_t n) {
|
||||
return 64 - CountLeadingZeros64(n);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void FftCompute(struct FftState* state, const int16_t* input,
|
||||
int input_scale_shift) {
|
||||
const size_t input_size = state->input_size;
|
||||
const size_t fft_size = state->fft_size;
|
||||
|
||||
int16_t* fft_input = state->input;
|
||||
// First, scale the input by the given shift.
|
||||
size_t i;
|
||||
for (i = 0; i < input_size; ++i) {
|
||||
fft_input[i] = static_cast<int16_t>(static_cast<uint16_t>(input[i])
|
||||
<< input_scale_shift);
|
||||
}
|
||||
// Zero out whatever else remains in the top part of the input.
|
||||
for (; i < fft_size; ++i) {
|
||||
fft_input[i] = 0;
|
||||
}
|
||||
|
||||
// Apply the FFT.
|
||||
kissfft_fixed16::kiss_fftr(
|
||||
reinterpret_cast<kissfft_fixed16::kiss_fftr_cfg>(state->scratch),
|
||||
state->input,
|
||||
reinterpret_cast<kissfft_fixed16::kiss_fft_cpx*>(state->output));
|
||||
}
|
||||
|
||||
void FftInit(struct FftState* state) {
|
||||
// All the initialization is done in FftPopulateState()
|
||||
}
|
||||
|
||||
void FftReset(struct FftState* state) {
|
||||
memset(state->input, 0, state->fft_size * sizeof(*state->input));
|
||||
memset(state->output, 0, (state->fft_size / 2 + 1) * sizeof(*state->output));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct complex_int16_t {
|
||||
int16_t real;
|
||||
int16_t imag;
|
||||
};
|
||||
|
||||
struct FftState {
|
||||
int16_t* input;
|
||||
struct complex_int16_t* output;
|
||||
size_t fft_size;
|
||||
size_t input_size;
|
||||
void* scratch;
|
||||
size_t scratch_size;
|
||||
};
|
||||
|
||||
void FftCompute(struct FftState* state, const int16_t* input,
|
||||
int input_scale_shift);
|
||||
|
||||
void FftInit(struct FftState* state);
|
||||
|
||||
void FftReset(struct FftState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int FftPopulateState(struct FftState* state, size_t input_size) {
|
||||
state->input_size = input_size;
|
||||
state->fft_size = 1;
|
||||
while (state->fft_size < state->input_size) {
|
||||
state->fft_size <<= 1;
|
||||
}
|
||||
|
||||
state->input = reinterpret_cast<int16_t*>(
|
||||
malloc(state->fft_size * sizeof(*state->input)));
|
||||
if (state->input == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft input buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->output = reinterpret_cast<complex_int16_t*>(
|
||||
malloc((state->fft_size / 2 + 1) * sizeof(*state->output) * 2));
|
||||
if (state->output == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft output buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ask kissfft how much memory it wants.
|
||||
size_t scratch_size = 0;
|
||||
kissfft_fixed16::kiss_fftr_cfg kfft_cfg = kissfft_fixed16::kiss_fftr_alloc(
|
||||
state->fft_size, 0, nullptr, &scratch_size);
|
||||
if (kfft_cfg != nullptr) {
|
||||
fprintf(stderr, "Kiss memory sizing failed.\n");
|
||||
return 0;
|
||||
}
|
||||
state->scratch = malloc(scratch_size);
|
||||
if (state->scratch == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft scratch buffer\n");
|
||||
return 0;
|
||||
}
|
||||
state->scratch_size = scratch_size;
|
||||
// Let kissfft configure the scratch space we just allocated
|
||||
kfft_cfg = kissfft_fixed16::kiss_fftr_alloc(state->fft_size, 0,
|
||||
state->scratch, &scratch_size);
|
||||
if (kfft_cfg != state->scratch) {
|
||||
fprintf(stderr, "Kiss memory preallocation strategy failed.\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FftFreeStateContents(struct FftState* state) {
|
||||
free(state->input);
|
||||
free(state->output);
|
||||
free(state->scratch);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Prepares and FFT for the given input size.
|
||||
int FftPopulateState(struct FftState* state, size_t input_size);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FftFreeStateContents(struct FftState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
@@ -0,0 +1,134 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state,
|
||||
struct complex_int16_t* fft_output,
|
||||
int32_t* energy) {
|
||||
const int end_index = state->end_index;
|
||||
int i;
|
||||
energy += state->start_index;
|
||||
fft_output += state->start_index;
|
||||
for (i = state->start_index; i < end_index; ++i) {
|
||||
const int32_t real = fft_output->real;
|
||||
const int32_t imag = fft_output->imag;
|
||||
fft_output++;
|
||||
const uint32_t mag_squared = (real * real) + (imag * imag);
|
||||
*energy++ = mag_squared;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterbankAccumulateChannels(struct FilterbankState* state,
|
||||
const int32_t* energy) {
|
||||
uint64_t* work = state->work;
|
||||
uint64_t weight_accumulator = 0;
|
||||
uint64_t unweight_accumulator = 0;
|
||||
|
||||
const int16_t* channel_frequency_starts = state->channel_frequency_starts;
|
||||
const int16_t* channel_weight_starts = state->channel_weight_starts;
|
||||
const int16_t* channel_widths = state->channel_widths;
|
||||
|
||||
int num_channels_plus_1 = state->num_channels + 1;
|
||||
int i;
|
||||
for (i = 0; i < num_channels_plus_1; ++i) {
|
||||
const int32_t* magnitudes = energy + *channel_frequency_starts++;
|
||||
const int16_t* weights = state->weights + *channel_weight_starts;
|
||||
const int16_t* unweights = state->unweights + *channel_weight_starts++;
|
||||
const int width = *channel_widths++;
|
||||
int j;
|
||||
for (j = 0; j < width; ++j) {
|
||||
weight_accumulator += *weights++ * ((uint64_t)*magnitudes);
|
||||
unweight_accumulator += *unweights++ * ((uint64_t)*magnitudes);
|
||||
++magnitudes;
|
||||
}
|
||||
*work++ = weight_accumulator;
|
||||
weight_accumulator = unweight_accumulator;
|
||||
unweight_accumulator = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t Sqrt32(uint32_t num) {
|
||||
if (num == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t res = 0;
|
||||
int max_bit_number = 32 - MostSignificantBit32(num);
|
||||
max_bit_number |= 1;
|
||||
uint32_t bit = 1U << (31 - max_bit_number);
|
||||
int iterations = (31 - max_bit_number) / 2 + 1;
|
||||
while (iterations--) {
|
||||
if (num >= res + bit) {
|
||||
num -= res + bit;
|
||||
res = (res >> 1U) + bit;
|
||||
} else {
|
||||
res >>= 1U;
|
||||
}
|
||||
bit >>= 2U;
|
||||
}
|
||||
// Do rounding - if we have the bits.
|
||||
if (num > res && res != 0xFFFF) {
|
||||
++res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint32_t Sqrt64(uint64_t num) {
|
||||
// Take a shortcut and just use 32 bit operations if the upper word is all
|
||||
// clear. This will cause a slight off by one issue for numbers close to 2^32,
|
||||
// but it probably isn't going to matter (and gives us a big performance win).
|
||||
if ((num >> 32) == 0) {
|
||||
return Sqrt32((uint32_t)num);
|
||||
}
|
||||
uint64_t res = 0;
|
||||
int max_bit_number = 64 - MostSignificantBit64(num);
|
||||
max_bit_number |= 1;
|
||||
uint64_t bit = 1ULL << (63 - max_bit_number);
|
||||
int iterations = (63 - max_bit_number) / 2 + 1;
|
||||
while (iterations--) {
|
||||
if (num >= res + bit) {
|
||||
num -= res + bit;
|
||||
res = (res >> 1U) + bit;
|
||||
} else {
|
||||
res >>= 1U;
|
||||
}
|
||||
bit >>= 2U;
|
||||
}
|
||||
// Do rounding - if we have the bits.
|
||||
if (num > res && res != 0xFFFFFFFFLL) {
|
||||
++res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift) {
|
||||
const int num_channels = state->num_channels;
|
||||
const uint64_t* work = state->work + 1;
|
||||
// Reuse the work buffer since we're fine clobbering it at this point to hold
|
||||
// the output.
|
||||
uint32_t* output = (uint32_t*)state->work;
|
||||
int i;
|
||||
for (i = 0; i < num_channels; ++i) {
|
||||
*output++ = Sqrt64(*work++) >> scale_down_shift;
|
||||
}
|
||||
return (uint32_t*)state->work;
|
||||
}
|
||||
|
||||
void FilterbankReset(struct FilterbankState* state) {
|
||||
memset(state->work, 0, (state->num_channels + 1) * sizeof(*state->work));
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
|
||||
#define kFilterbankBits 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FilterbankState {
|
||||
int num_channels;
|
||||
int start_index;
|
||||
int end_index;
|
||||
int16_t* channel_frequency_starts;
|
||||
int16_t* channel_weight_starts;
|
||||
int16_t* channel_widths;
|
||||
int16_t* weights;
|
||||
int16_t* unweights;
|
||||
uint64_t* work;
|
||||
};
|
||||
|
||||
// Converts the relevant complex values of an FFT output into energy (the
|
||||
// square magnitude).
|
||||
void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state,
|
||||
struct complex_int16_t* fft_output,
|
||||
int32_t* energy);
|
||||
|
||||
// Computes the mel-scale filterbank on the given energy array. Output is cached
|
||||
// internally - to fetch it, you need to call FilterbankSqrt.
|
||||
void FilterbankAccumulateChannels(struct FilterbankState* state,
|
||||
const int32_t* energy);
|
||||
|
||||
// Applies an integer square root to the 64 bit intermediate values of the
|
||||
// filterbank, and returns a pointer to them. Memory will be invalidated the
|
||||
// next time FilterbankAccumulateChannels is called.
|
||||
uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift);
|
||||
|
||||
void FilterbankReset(struct FilterbankState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
@@ -0,0 +1,220 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define kFilterbankIndexAlignment 4
|
||||
#define kFilterbankChannelBlockSize 4
|
||||
|
||||
void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config) {
|
||||
config->num_channels = 32;
|
||||
config->lower_band_limit = 125.0f;
|
||||
config->upper_band_limit = 7500.0f;
|
||||
config->output_scale_shift = 7;
|
||||
}
|
||||
|
||||
static float FreqToMel(float freq) { return 1127.0 * log1p(freq / 700.0); }
|
||||
|
||||
static void CalculateCenterFrequencies(const int num_channels,
|
||||
const float lower_frequency_limit,
|
||||
const float upper_frequency_limit,
|
||||
float* center_frequencies) {
|
||||
assert(lower_frequency_limit >= 0.0f);
|
||||
assert(upper_frequency_limit > lower_frequency_limit);
|
||||
|
||||
const float mel_low = FreqToMel(lower_frequency_limit);
|
||||
const float mel_hi = FreqToMel(upper_frequency_limit);
|
||||
const float mel_span = mel_hi - mel_low;
|
||||
const float mel_spacing = mel_span / ((float)num_channels);
|
||||
int i;
|
||||
for (i = 0; i < num_channels; ++i) {
|
||||
center_frequencies[i] = mel_low + (mel_spacing * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void QuantizeFilterbankWeights(const float float_weight, int16_t* weight,
|
||||
int16_t* unweight) {
|
||||
*weight = floor(float_weight * (1 << kFilterbankBits) + 0.5);
|
||||
*unweight = floor((1.0 - float_weight) * (1 << kFilterbankBits) + 0.5);
|
||||
}
|
||||
|
||||
int FilterbankPopulateState(const struct FilterbankConfig* config,
|
||||
struct FilterbankState* state, int sample_rate,
|
||||
int spectrum_size) {
|
||||
state->num_channels = config->num_channels;
|
||||
const int num_channels_plus_1 = config->num_channels + 1;
|
||||
|
||||
// How should we align things to index counts given the byte alignment?
|
||||
const int index_alignment =
|
||||
(kFilterbankIndexAlignment < sizeof(int16_t)
|
||||
? 1
|
||||
: kFilterbankIndexAlignment / sizeof(int16_t));
|
||||
|
||||
state->channel_frequency_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_frequency_starts));
|
||||
state->channel_weight_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_weight_starts));
|
||||
state->channel_widths =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_widths));
|
||||
state->work = malloc(num_channels_plus_1 * sizeof(*state->work));
|
||||
|
||||
float* center_mel_freqs =
|
||||
malloc(num_channels_plus_1 * sizeof(*center_mel_freqs));
|
||||
int16_t* actual_channel_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*actual_channel_starts));
|
||||
int16_t* actual_channel_widths =
|
||||
malloc(num_channels_plus_1 * sizeof(*actual_channel_widths));
|
||||
|
||||
if (state->channel_frequency_starts == NULL ||
|
||||
state->channel_weight_starts == NULL || state->channel_widths == NULL ||
|
||||
center_mel_freqs == NULL || actual_channel_starts == NULL ||
|
||||
actual_channel_widths == NULL) {
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
fprintf(stderr, "Failed to allocate channel buffers\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
CalculateCenterFrequencies(num_channels_plus_1, config->lower_band_limit,
|
||||
config->upper_band_limit, center_mel_freqs);
|
||||
|
||||
// Always exclude DC.
|
||||
const float hz_per_sbin = 0.5 * sample_rate / ((float)spectrum_size - 1);
|
||||
state->start_index = 1.5 + config->lower_band_limit / hz_per_sbin;
|
||||
state->end_index = 0; // Initialized to zero here, but actually set below.
|
||||
|
||||
// For each channel, we need to figure out what frequencies belong to it, and
|
||||
// how much padding we need to add so that we can efficiently multiply the
|
||||
// weights and unweights for accumulation. To simplify the multiplication
|
||||
// logic, all channels will have some multiplication to do (even if there are
|
||||
// no frequencies that accumulate to that channel) - they will be directed to
|
||||
// a set of zero weights.
|
||||
int chan_freq_index_start = state->start_index;
|
||||
int weight_index_start = 0;
|
||||
int needs_zeros = 0;
|
||||
|
||||
int chan;
|
||||
for (chan = 0; chan < num_channels_plus_1; ++chan) {
|
||||
// Keep jumping frequencies until we overshoot the bound on this channel.
|
||||
int freq_index = chan_freq_index_start;
|
||||
while (FreqToMel((freq_index)*hz_per_sbin) <= center_mel_freqs[chan]) {
|
||||
++freq_index;
|
||||
}
|
||||
|
||||
const int width = freq_index - chan_freq_index_start;
|
||||
actual_channel_starts[chan] = chan_freq_index_start;
|
||||
actual_channel_widths[chan] = width;
|
||||
|
||||
if (width == 0) {
|
||||
// This channel doesn't actually get anything from the frequencies, it's
|
||||
// always zero. We need then to insert some 'zero' weights into the
|
||||
// output, and just redirect this channel to do a single multiplication at
|
||||
// this point. For simplicity, the zeros are placed at the beginning of
|
||||
// the weights arrays, so we have to go and update all the other
|
||||
// weight_starts to reflect this shift (but only once).
|
||||
state->channel_frequency_starts[chan] = 0;
|
||||
state->channel_weight_starts[chan] = 0;
|
||||
state->channel_widths[chan] = kFilterbankChannelBlockSize;
|
||||
if (!needs_zeros) {
|
||||
needs_zeros = 1;
|
||||
int j;
|
||||
for (j = 0; j < chan; ++j) {
|
||||
state->channel_weight_starts[j] += kFilterbankChannelBlockSize;
|
||||
}
|
||||
weight_index_start += kFilterbankChannelBlockSize;
|
||||
}
|
||||
} else {
|
||||
// How far back do we need to go to ensure that we have the proper
|
||||
// alignment?
|
||||
const int aligned_start =
|
||||
(chan_freq_index_start / index_alignment) * index_alignment;
|
||||
const int aligned_width = (chan_freq_index_start - aligned_start + width);
|
||||
const int padded_width =
|
||||
(((aligned_width - 1) / kFilterbankChannelBlockSize) + 1) *
|
||||
kFilterbankChannelBlockSize;
|
||||
|
||||
state->channel_frequency_starts[chan] = aligned_start;
|
||||
state->channel_weight_starts[chan] = weight_index_start;
|
||||
state->channel_widths[chan] = padded_width;
|
||||
weight_index_start += padded_width;
|
||||
}
|
||||
chan_freq_index_start = freq_index;
|
||||
}
|
||||
|
||||
// Allocate the two arrays to store the weights - weight_index_start contains
|
||||
// the index of what would be the next set of weights that we would need to
|
||||
// add, so that's how many weights we need to allocate.
|
||||
state->weights = calloc(weight_index_start, sizeof(*state->weights));
|
||||
state->unweights = calloc(weight_index_start, sizeof(*state->unweights));
|
||||
|
||||
// If the alloc failed, we also need to nuke the arrays.
|
||||
if (state->weights == NULL || state->unweights == NULL) {
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
fprintf(stderr, "Failed to allocate weights or unweights\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Next pass, compute all the weights. Since everything has been memset to
|
||||
// zero, we only need to fill in the weights that correspond to some frequency
|
||||
// for a channel.
|
||||
const float mel_low = FreqToMel(config->lower_band_limit);
|
||||
for (chan = 0; chan < num_channels_plus_1; ++chan) {
|
||||
int frequency = actual_channel_starts[chan];
|
||||
const int num_frequencies = actual_channel_widths[chan];
|
||||
const int frequency_offset =
|
||||
frequency - state->channel_frequency_starts[chan];
|
||||
const int weight_start = state->channel_weight_starts[chan];
|
||||
const float denom_val = (chan == 0) ? mel_low : center_mel_freqs[chan - 1];
|
||||
|
||||
int j;
|
||||
for (j = 0; j < num_frequencies; ++j, ++frequency) {
|
||||
const float weight =
|
||||
(center_mel_freqs[chan] - FreqToMel(frequency * hz_per_sbin)) /
|
||||
(center_mel_freqs[chan] - denom_val);
|
||||
|
||||
// Make the float into an integer for the weights (and unweights).
|
||||
const int weight_index = weight_start + frequency_offset + j;
|
||||
QuantizeFilterbankWeights(weight, state->weights + weight_index,
|
||||
state->unweights + weight_index);
|
||||
}
|
||||
if (frequency > state->end_index) {
|
||||
state->end_index = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
if (state->end_index >= spectrum_size) {
|
||||
fprintf(stderr, "Filterbank end_index is above spectrum size.\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FilterbankFreeStateContents(struct FilterbankState* state) {
|
||||
free(state->channel_frequency_starts);
|
||||
free(state->channel_weight_starts);
|
||||
free(state->channel_widths);
|
||||
free(state->weights);
|
||||
free(state->unweights);
|
||||
free(state->work);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FilterbankConfig {
|
||||
// number of frequency channel buckets for filterbank
|
||||
int num_channels;
|
||||
// maximum frequency to include
|
||||
float upper_band_limit;
|
||||
// minimum frequency to include
|
||||
float lower_band_limit;
|
||||
// unused
|
||||
int output_scale_shift;
|
||||
};
|
||||
|
||||
// Fills the frontendConfig with "sane" defaults.
|
||||
void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int FilterbankPopulateState(const struct FilterbankConfig* config,
|
||||
struct FilterbankState* state, int sample_rate,
|
||||
int spectrum_size);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FilterbankFreeStateContents(struct FilterbankState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
struct FrontendOutput FrontendProcessSamples(struct FrontendState* state,
|
||||
const int16_t* samples,
|
||||
size_t num_samples,
|
||||
size_t* num_samples_read) {
|
||||
struct FrontendOutput output;
|
||||
output.values = NULL;
|
||||
output.size = 0;
|
||||
|
||||
// Try to apply the window - if it fails, return and wait for more data.
|
||||
if (!WindowProcessSamples(&state->window, samples, num_samples,
|
||||
num_samples_read)) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// Apply the FFT to the window's output (and scale it so that the fixed point
|
||||
// FFT can have as much resolution as possible).
|
||||
int input_shift =
|
||||
15 - MostSignificantBit32(state->window.max_abs_output_value);
|
||||
FftCompute(&state->fft, state->window.output, input_shift);
|
||||
|
||||
// We can re-ruse the fft's output buffer to hold the energy.
|
||||
int32_t* energy = (int32_t*)state->fft.output;
|
||||
|
||||
FilterbankConvertFftComplexToEnergy(&state->filterbank, state->fft.output,
|
||||
energy);
|
||||
|
||||
FilterbankAccumulateChannels(&state->filterbank, energy);
|
||||
uint32_t* scaled_filterbank = FilterbankSqrt(&state->filterbank, input_shift);
|
||||
|
||||
// Apply noise reduction.
|
||||
NoiseReductionApply(&state->noise_reduction, scaled_filterbank);
|
||||
|
||||
if (state->pcan_gain_control.enable_pcan) {
|
||||
PcanGainControlApply(&state->pcan_gain_control, scaled_filterbank);
|
||||
}
|
||||
|
||||
// Apply the log and scale.
|
||||
int correction_bits =
|
||||
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
|
||||
uint16_t* logged_filterbank =
|
||||
LogScaleApply(&state->log_scale, scaled_filterbank,
|
||||
state->filterbank.num_channels, correction_bits);
|
||||
|
||||
output.size = state->filterbank.num_channels;
|
||||
output.values = logged_filterbank;
|
||||
return output;
|
||||
}
|
||||
|
||||
void FrontendReset(struct FrontendState* state) {
|
||||
WindowReset(&state->window);
|
||||
FftReset(&state->fft);
|
||||
FilterbankReset(&state->filterbank);
|
||||
NoiseReductionReset(&state->noise_reduction);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FrontendState {
|
||||
struct WindowState window;
|
||||
struct FftState fft;
|
||||
struct FilterbankState filterbank;
|
||||
struct NoiseReductionState noise_reduction;
|
||||
struct PcanGainControlState pcan_gain_control;
|
||||
struct LogScaleState log_scale;
|
||||
};
|
||||
|
||||
struct FrontendOutput {
|
||||
const uint16_t* values;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Main entry point to processing frontend samples. Updates num_samples_read to
|
||||
// contain the number of samples that have been consumed from the input array.
|
||||
// Returns a struct containing the generated output. If not enough samples were
|
||||
// added to generate a feature vector, the returned size will be 0 and the
|
||||
// values pointer will be NULL. Note that the output pointer will be invalidated
|
||||
// as soon as FrontendProcessSamples is called again, so copy the contents
|
||||
// elsewhere if you need to use them later.
|
||||
struct FrontendOutput FrontendProcessSamples(struct FrontendState* state,
|
||||
const int16_t* samples,
|
||||
size_t num_samples,
|
||||
size_t* num_samples_read);
|
||||
|
||||
void FrontendReset(struct FrontendState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
@@ -0,0 +1,85 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
void FrontendFillConfigWithDefaults(struct FrontendConfig* config) {
|
||||
WindowFillConfigWithDefaults(&config->window);
|
||||
FilterbankFillConfigWithDefaults(&config->filterbank);
|
||||
NoiseReductionFillConfigWithDefaults(&config->noise_reduction);
|
||||
PcanGainControlFillConfigWithDefaults(&config->pcan_gain_control);
|
||||
LogScaleFillConfigWithDefaults(&config->log_scale);
|
||||
}
|
||||
|
||||
int FrontendPopulateState(const struct FrontendConfig* config,
|
||||
struct FrontendState* state, int sample_rate) {
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
if (!WindowPopulateState(&config->window, &state->window, sample_rate)) {
|
||||
fprintf(stderr, "Failed to populate window state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FftPopulateState(&state->fft, state->window.size)) {
|
||||
fprintf(stderr, "Failed to populate fft state\n");
|
||||
return 0;
|
||||
}
|
||||
FftInit(&state->fft);
|
||||
|
||||
if (!FilterbankPopulateState(&config->filterbank, &state->filterbank,
|
||||
sample_rate, state->fft.fft_size / 2 + 1)) {
|
||||
fprintf(stderr, "Failed to populate filterbank state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!NoiseReductionPopulateState(&config->noise_reduction,
|
||||
&state->noise_reduction,
|
||||
state->filterbank.num_channels)) {
|
||||
fprintf(stderr, "Failed to populate noise reduction state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_correction_bits =
|
||||
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
|
||||
if (!PcanGainControlPopulateState(
|
||||
&config->pcan_gain_control, &state->pcan_gain_control,
|
||||
state->noise_reduction.estimate, state->filterbank.num_channels,
|
||||
state->noise_reduction.smoothing_bits, input_correction_bits)) {
|
||||
fprintf(stderr, "Failed to populate pcan gain control state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!LogScalePopulateState(&config->log_scale, &state->log_scale)) {
|
||||
fprintf(stderr, "Failed to populate log scale state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FrontendReset(state);
|
||||
|
||||
// All good, return a true value.
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FrontendFreeStateContents(struct FrontendState* state) {
|
||||
WindowFreeStateContents(&state->window);
|
||||
FftFreeStateContents(&state->fft);
|
||||
FilterbankFreeStateContents(&state->filterbank);
|
||||
NoiseReductionFreeStateContents(&state->noise_reduction);
|
||||
PcanGainControlFreeStateContents(&state->pcan_gain_control);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FrontendConfig {
|
||||
struct WindowConfig window;
|
||||
struct FilterbankConfig filterbank;
|
||||
struct NoiseReductionConfig noise_reduction;
|
||||
struct PcanGainControlConfig pcan_gain_control;
|
||||
struct LogScaleConfig log_scale;
|
||||
};
|
||||
|
||||
// Fills the frontendConfig with "sane" defaults.
|
||||
void FrontendFillConfigWithDefaults(struct FrontendConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int FrontendPopulateState(const struct FrontendConfig* config,
|
||||
struct FrontendState* state, int sample_rate);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FrontendFreeStateContents(struct FrontendState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
|
||||
// This header file should be included in all variants of kiss_fft_$type.{h,cc}
|
||||
// so that their sub-included source files do not mistakenly wrap libc header
|
||||
// files within their kissfft_$type namespaces.
|
||||
// E.g, This header avoids kissfft_int16.h containing:
|
||||
// namespace kiss_fft_int16 {
|
||||
// #include "third_party/kissfft/kiss_fft.h"
|
||||
// }
|
||||
// where kiss_fft_.h contains:
|
||||
// #include <math.h>
|
||||
//
|
||||
// TRICK: By including the following header files here, their preprocessor
|
||||
// header guards prevent them being re-defined inside of the kiss_fft_$type
|
||||
// namespaces declared within the kiss_fft_$type.{h,cc} sources.
|
||||
// Note that the original kiss_fft*.h files are untouched since they
|
||||
// may be used in libraries that include them directly.
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SIMD
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
#define FIXED_POINT 16
|
||||
namespace kissfft_fixed16 {
|
||||
#include "third_party/kissfft/kiss_fft.c"
|
||||
#include "third_party/kissfft/tools/kiss_fftr.c"
|
||||
} // namespace kissfft_fixed16
|
||||
#undef FIXED_POINT
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
// Wrap 16-bit kiss fft in its own namespace. Enables us to link an application
|
||||
// with different kiss fft resultions (16/32 bit interger, float, double)
|
||||
// without getting a linker error.
|
||||
#define FIXED_POINT 16
|
||||
namespace kissfft_fixed16 {
|
||||
#include "third_party/kissfft/kiss_fft.h"
|
||||
#include "third_party/kissfft/tools/kiss_fftr.h"
|
||||
} // namespace kissfft_fixed16
|
||||
#undef FIXED_POINT
|
||||
#undef kiss_fft_scalar
|
||||
#undef KISS_FFT_H
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
|
||||
const uint16_t kLogLut[]
|
||||
#ifndef _MSC_VER
|
||||
__attribute__((aligned(4)))
|
||||
#endif // _MSV_VER
|
||||
= {0, 224, 442, 654, 861, 1063, 1259, 1450, 1636, 1817, 1992, 2163,
|
||||
2329, 2490, 2646, 2797, 2944, 3087, 3224, 3358, 3487, 3611, 3732, 3848,
|
||||
3960, 4068, 4172, 4272, 4368, 4460, 4549, 4633, 4714, 4791, 4864, 4934,
|
||||
5001, 5063, 5123, 5178, 5231, 5280, 5326, 5368, 5408, 5444, 5477, 5507,
|
||||
5533, 5557, 5578, 5595, 5610, 5622, 5631, 5637, 5640, 5641, 5638, 5633,
|
||||
5626, 5615, 5602, 5586, 5568, 5547, 5524, 5498, 5470, 5439, 5406, 5370,
|
||||
5332, 5291, 5249, 5203, 5156, 5106, 5054, 5000, 4944, 4885, 4825, 4762,
|
||||
4697, 4630, 4561, 4490, 4416, 4341, 4264, 4184, 4103, 4020, 3935, 3848,
|
||||
3759, 3668, 3575, 3481, 3384, 3286, 3186, 3084, 2981, 2875, 2768, 2659,
|
||||
2549, 2437, 2323, 2207, 2090, 1971, 1851, 1729, 1605, 1480, 1353, 1224,
|
||||
1094, 963, 830, 695, 559, 421, 282, 142, 0, 0};
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Number of segments in the log lookup table. The table will be kLogSegments+1
|
||||
// in length (with some padding).
|
||||
#define kLogSegments 128
|
||||
#define kLogSegmentsLog2 7
|
||||
|
||||
// Scale used by lookup table.
|
||||
#define kLogScale 65536
|
||||
#define kLogScaleLog2 16
|
||||
#define kLogCoeff 45426
|
||||
|
||||
extern const uint16_t kLogLut[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
@@ -0,0 +1,83 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
|
||||
|
||||
#define kuint16max 0x0000FFFF
|
||||
|
||||
// The following functions implement integer logarithms of various sizes. The
|
||||
// approximation is calculated according to method described in
|
||||
// www.inti.gob.ar/electronicaeinformatica/instrumentacion/utic/
|
||||
// publicaciones/SPL2007/Log10-spl07.pdf
|
||||
// It first calculates log2 of the input and then converts it to natural
|
||||
// logarithm.
|
||||
|
||||
static uint32_t Log2FractionPart(const uint32_t x, const uint32_t log2x) {
|
||||
// Part 1
|
||||
int32_t frac = x - (1LL << log2x);
|
||||
if (log2x < kLogScaleLog2) {
|
||||
frac <<= kLogScaleLog2 - log2x;
|
||||
} else {
|
||||
frac >>= log2x - kLogScaleLog2;
|
||||
}
|
||||
// Part 2
|
||||
const uint32_t base_seg = frac >> (kLogScaleLog2 - kLogSegmentsLog2);
|
||||
const uint32_t seg_unit =
|
||||
(((uint32_t)1) << kLogScaleLog2) >> kLogSegmentsLog2;
|
||||
|
||||
const int32_t c0 = kLogLut[base_seg];
|
||||
const int32_t c1 = kLogLut[base_seg + 1];
|
||||
const int32_t seg_base = seg_unit * base_seg;
|
||||
const int32_t rel_pos = ((c1 - c0) * (frac - seg_base)) >> kLogScaleLog2;
|
||||
return frac + c0 + rel_pos;
|
||||
}
|
||||
|
||||
static uint32_t Log(const uint32_t x, const uint32_t scale_shift) {
|
||||
const uint32_t integer = MostSignificantBit32(x) - 1;
|
||||
const uint32_t fraction = Log2FractionPart(x, integer);
|
||||
const uint32_t log2 = (integer << kLogScaleLog2) + fraction;
|
||||
const uint32_t round = kLogScale / 2;
|
||||
const uint32_t loge = (((uint64_t)kLogCoeff) * log2 + round) >> kLogScaleLog2;
|
||||
// Finally scale to our output scale
|
||||
const uint32_t loge_scaled = ((loge << scale_shift) + round) >> kLogScaleLog2;
|
||||
return loge_scaled;
|
||||
}
|
||||
|
||||
uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal,
|
||||
int signal_size, int correction_bits) {
|
||||
const int scale_shift = state->scale_shift;
|
||||
uint16_t* output = (uint16_t*)signal;
|
||||
uint16_t* ret = output;
|
||||
int i;
|
||||
for (i = 0; i < signal_size; ++i) {
|
||||
uint32_t value = *signal++;
|
||||
if (state->enable_log) {
|
||||
if (correction_bits < 0) {
|
||||
value >>= -correction_bits;
|
||||
} else {
|
||||
value <<= correction_bits;
|
||||
}
|
||||
if (value > 1) {
|
||||
value = Log(value, scale_shift);
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
*output++ = (value < kuint16max) ? value : kuint16max;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct LogScaleState {
|
||||
int enable_log;
|
||||
int scale_shift;
|
||||
};
|
||||
|
||||
// Applies a fixed point logarithm to the signal and converts it to 16 bit. Note
|
||||
// that the signal array will be modified.
|
||||
uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal,
|
||||
int signal_size, int correction_bits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
|
||||
|
||||
void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config) {
|
||||
config->enable_log = 1;
|
||||
config->scale_shift = 6;
|
||||
}
|
||||
|
||||
int LogScalePopulateState(const struct LogScaleConfig* config,
|
||||
struct LogScaleState* state) {
|
||||
state->enable_log = config->enable_log;
|
||||
state->scale_shift = config->scale_shift;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct LogScaleConfig {
|
||||
// set to false (0) to disable this module
|
||||
int enable_log;
|
||||
// scale results by 2^(scale_shift)
|
||||
int scale_shift;
|
||||
};
|
||||
|
||||
// Populates the LogScaleConfig with "sane" default values.
|
||||
void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int LogScalePopulateState(const struct LogScaleConfig* config,
|
||||
struct LogScaleState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal) {
|
||||
int i;
|
||||
for (i = 0; i < state->num_channels; ++i) {
|
||||
const uint32_t smoothing =
|
||||
((i & 1) == 0) ? state->even_smoothing : state->odd_smoothing;
|
||||
const uint32_t one_minus_smoothing = (1 << kNoiseReductionBits) - smoothing;
|
||||
|
||||
// Update the estimate of the noise.
|
||||
const uint32_t signal_scaled_up = signal[i] << state->smoothing_bits;
|
||||
uint32_t estimate =
|
||||
(((uint64_t)signal_scaled_up * smoothing) +
|
||||
((uint64_t)state->estimate[i] * one_minus_smoothing)) >>
|
||||
kNoiseReductionBits;
|
||||
state->estimate[i] = estimate;
|
||||
|
||||
// Make sure that we can't get a negative value for the signal - estimate.
|
||||
if (estimate > signal_scaled_up) {
|
||||
estimate = signal_scaled_up;
|
||||
}
|
||||
|
||||
const uint32_t floor =
|
||||
((uint64_t)signal[i] * state->min_signal_remaining) >>
|
||||
kNoiseReductionBits;
|
||||
const uint32_t subtracted =
|
||||
(signal_scaled_up - estimate) >> state->smoothing_bits;
|
||||
const uint32_t output = subtracted > floor ? subtracted : floor;
|
||||
signal[i] = output;
|
||||
}
|
||||
}
|
||||
|
||||
void NoiseReductionReset(struct NoiseReductionState* state) {
|
||||
memset(state->estimate, 0, sizeof(*state->estimate) * state->num_channels);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
|
||||
#define kNoiseReductionBits 14
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NoiseReductionState {
|
||||
int smoothing_bits;
|
||||
uint16_t even_smoothing;
|
||||
uint16_t odd_smoothing;
|
||||
uint16_t min_signal_remaining;
|
||||
int num_channels;
|
||||
uint32_t* estimate;
|
||||
};
|
||||
|
||||
// Removes stationary noise from each channel of the signal using a low pass
|
||||
// filter.
|
||||
void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal);
|
||||
|
||||
void NoiseReductionReset(struct NoiseReductionState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config) {
|
||||
config->smoothing_bits = 10;
|
||||
config->even_smoothing = 0.025;
|
||||
config->odd_smoothing = 0.06;
|
||||
config->min_signal_remaining = 0.05;
|
||||
}
|
||||
|
||||
int NoiseReductionPopulateState(const struct NoiseReductionConfig* config,
|
||||
struct NoiseReductionState* state,
|
||||
int num_channels) {
|
||||
state->smoothing_bits = config->smoothing_bits;
|
||||
state->odd_smoothing = config->odd_smoothing * (1 << kNoiseReductionBits);
|
||||
state->even_smoothing = config->even_smoothing * (1 << kNoiseReductionBits);
|
||||
state->min_signal_remaining =
|
||||
config->min_signal_remaining * (1 << kNoiseReductionBits);
|
||||
state->num_channels = num_channels;
|
||||
state->estimate = calloc(state->num_channels, sizeof(*state->estimate));
|
||||
if (state->estimate == NULL) {
|
||||
fprintf(stderr, "Failed to alloc estimate buffer\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void NoiseReductionFreeStateContents(struct NoiseReductionState* state) {
|
||||
free(state->estimate);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NoiseReductionConfig {
|
||||
// scale the signal up by 2^(smoothing_bits) before reduction
|
||||
int smoothing_bits;
|
||||
// smoothing coefficient for even-numbered channels
|
||||
float even_smoothing;
|
||||
// smoothing coefficient for odd-numbered channels
|
||||
float odd_smoothing;
|
||||
// fraction of signal to preserve (1.0 disables this module)
|
||||
float min_signal_remaining;
|
||||
};
|
||||
|
||||
// Populates the NoiseReductionConfig with "sane" default values.
|
||||
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int NoiseReductionPopulateState(const struct NoiseReductionConfig* config,
|
||||
struct NoiseReductionState* state,
|
||||
int num_channels);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void NoiseReductionFreeStateContents(struct NoiseReductionState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut) {
|
||||
if (x <= 2) {
|
||||
return lut[x];
|
||||
}
|
||||
|
||||
const int16_t interval = MostSignificantBit32(x);
|
||||
lut += 4 * interval - 6;
|
||||
|
||||
const int16_t frac =
|
||||
((interval < 11) ? (x << (11 - interval)) : (x >> (interval - 11))) &
|
||||
0x3FF;
|
||||
|
||||
int32_t result = ((int32_t)lut[2] * frac) >> 5;
|
||||
result += (int32_t)((uint32_t)lut[1] << 5);
|
||||
result *= frac;
|
||||
result = (result + (1 << 14)) >> 15;
|
||||
result += lut[0];
|
||||
return (int16_t)result;
|
||||
}
|
||||
|
||||
uint32_t PcanShrink(const uint32_t x) {
|
||||
if (x < (2 << kPcanSnrBits)) {
|
||||
return (x * x) >> (2 + 2 * kPcanSnrBits - kPcanOutputBits);
|
||||
} else {
|
||||
return (x >> (kPcanSnrBits - kPcanOutputBits)) - (1 << kPcanOutputBits);
|
||||
}
|
||||
}
|
||||
|
||||
void PcanGainControlApply(struct PcanGainControlState* state,
|
||||
uint32_t* signal) {
|
||||
int i;
|
||||
for (i = 0; i < state->num_channels; ++i) {
|
||||
const uint32_t gain =
|
||||
WideDynamicFunction(state->noise_estimate[i], state->gain_lut);
|
||||
const uint32_t snr = ((uint64_t)signal[i] * gain) >> state->snr_shift;
|
||||
signal[i] = PcanShrink(snr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kPcanSnrBits 12
|
||||
#define kPcanOutputBits 6
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Details at https://research.google/pubs/pub45911.pdf
|
||||
struct PcanGainControlState {
|
||||
int enable_pcan;
|
||||
uint32_t* noise_estimate;
|
||||
int num_channels;
|
||||
int16_t* gain_lut;
|
||||
int32_t snr_shift;
|
||||
};
|
||||
|
||||
int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut);
|
||||
|
||||
uint32_t PcanShrink(const uint32_t x);
|
||||
|
||||
void PcanGainControlApply(struct PcanGainControlState* state, uint32_t* signal);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define kint16max 0x00007FFF
|
||||
|
||||
void PcanGainControlFillConfigWithDefaults(
|
||||
struct PcanGainControlConfig* config) {
|
||||
config->enable_pcan = 0;
|
||||
config->strength = 0.95;
|
||||
config->offset = 80.0;
|
||||
config->gain_bits = 21;
|
||||
}
|
||||
|
||||
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config,
|
||||
int32_t input_bits, uint32_t x) {
|
||||
const float x_as_float = ((float)x) / ((uint32_t)1 << input_bits);
|
||||
const float gain_as_float =
|
||||
((uint32_t)1 << config->gain_bits) *
|
||||
powf(x_as_float + config->offset, -config->strength);
|
||||
|
||||
if (gain_as_float > kint16max) {
|
||||
return kint16max;
|
||||
}
|
||||
return (int16_t)(gain_as_float + 0.5f);
|
||||
}
|
||||
|
||||
int PcanGainControlPopulateState(const struct PcanGainControlConfig* config,
|
||||
struct PcanGainControlState* state,
|
||||
uint32_t* noise_estimate,
|
||||
const int num_channels,
|
||||
const uint16_t smoothing_bits,
|
||||
const int32_t input_correction_bits) {
|
||||
state->enable_pcan = config->enable_pcan;
|
||||
if (!state->enable_pcan) {
|
||||
return 1;
|
||||
}
|
||||
state->noise_estimate = noise_estimate;
|
||||
state->num_channels = num_channels;
|
||||
state->gain_lut = malloc(kWideDynamicFunctionLUTSize * sizeof(int16_t));
|
||||
if (state->gain_lut == NULL) {
|
||||
fprintf(stderr, "Failed to allocate gain LUT\n");
|
||||
return 0;
|
||||
}
|
||||
state->snr_shift = config->gain_bits - input_correction_bits - kPcanSnrBits;
|
||||
|
||||
const int32_t input_bits = smoothing_bits - input_correction_bits;
|
||||
state->gain_lut[0] = PcanGainLookupFunction(config, input_bits, 0);
|
||||
state->gain_lut[1] = PcanGainLookupFunction(config, input_bits, 1);
|
||||
state->gain_lut -= 6;
|
||||
int interval;
|
||||
for (interval = 2; interval <= kWideDynamicFunctionBits; ++interval) {
|
||||
const uint32_t x0 = (uint32_t)1 << (interval - 1);
|
||||
const uint32_t x1 = x0 + (x0 >> 1);
|
||||
const uint32_t x2 =
|
||||
(interval == kWideDynamicFunctionBits) ? x0 + (x0 - 1) : 2 * x0;
|
||||
|
||||
const int16_t y0 = PcanGainLookupFunction(config, input_bits, x0);
|
||||
const int16_t y1 = PcanGainLookupFunction(config, input_bits, x1);
|
||||
const int16_t y2 = PcanGainLookupFunction(config, input_bits, x2);
|
||||
|
||||
const int32_t diff1 = (int32_t)y1 - y0;
|
||||
const int32_t diff2 = (int32_t)y2 - y0;
|
||||
const int32_t a1 = 4 * diff1 - diff2;
|
||||
const int32_t a2 = diff2 - a1;
|
||||
|
||||
state->gain_lut[4 * interval] = y0;
|
||||
state->gain_lut[4 * interval + 1] = (int16_t)a1;
|
||||
state->gain_lut[4 * interval + 2] = (int16_t)a2;
|
||||
}
|
||||
state->gain_lut += 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PcanGainControlFreeStateContents(struct PcanGainControlState* state) {
|
||||
free(state->gain_lut);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
|
||||
#define kWideDynamicFunctionBits 32
|
||||
#define kWideDynamicFunctionLUTSize (4 * kWideDynamicFunctionBits - 3)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct PcanGainControlConfig {
|
||||
// set to false (0) to disable this module
|
||||
int enable_pcan;
|
||||
// gain normalization exponent (0.0 disables, 1.0 full strength)
|
||||
float strength;
|
||||
// positive value added in the normalization denominator
|
||||
float offset;
|
||||
// number of fractional bits in the gain
|
||||
int gain_bits;
|
||||
};
|
||||
|
||||
void PcanGainControlFillConfigWithDefaults(
|
||||
struct PcanGainControlConfig* config);
|
||||
|
||||
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config,
|
||||
int32_t input_bits, uint32_t x);
|
||||
|
||||
int PcanGainControlPopulateState(const struct PcanGainControlConfig* config,
|
||||
struct PcanGainControlState* state,
|
||||
uint32_t* noise_estimate,
|
||||
const int num_channels,
|
||||
const uint16_t smoothing_bits,
|
||||
const int32_t input_correction_bits);
|
||||
|
||||
void PcanGainControlFreeStateContents(struct PcanGainControlState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
@@ -0,0 +1,70 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int WindowProcessSamples(struct WindowState* state, const int16_t* samples,
|
||||
size_t num_samples, size_t* num_samples_read) {
|
||||
const int size = state->size;
|
||||
|
||||
// Copy samples from the samples buffer over to our local input.
|
||||
size_t max_samples_to_copy = state->size - state->input_used;
|
||||
if (max_samples_to_copy > num_samples) {
|
||||
max_samples_to_copy = num_samples;
|
||||
}
|
||||
memcpy(state->input + state->input_used, samples,
|
||||
max_samples_to_copy * sizeof(*samples));
|
||||
*num_samples_read = max_samples_to_copy;
|
||||
state->input_used += max_samples_to_copy;
|
||||
|
||||
if (state->input_used < state->size) {
|
||||
// We don't have enough samples to compute a window.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply the window to the input.
|
||||
const int16_t* coefficients = state->coefficients;
|
||||
const int16_t* input = state->input;
|
||||
int16_t* output = state->output;
|
||||
int i;
|
||||
int16_t max_abs_output_value = 0;
|
||||
for (i = 0; i < size; ++i) {
|
||||
int16_t new_value =
|
||||
(((int32_t)*input++) * *coefficients++) >> kFrontendWindowBits;
|
||||
*output++ = new_value;
|
||||
if (new_value < 0) {
|
||||
new_value = -new_value;
|
||||
}
|
||||
if (new_value > max_abs_output_value) {
|
||||
max_abs_output_value = new_value;
|
||||
}
|
||||
}
|
||||
// Shuffle the input down by the step size, and update how much we have used.
|
||||
memmove(state->input, state->input + state->step,
|
||||
sizeof(*state->input) * (state->size - state->step));
|
||||
state->input_used -= state->step;
|
||||
state->max_abs_output_value = max_abs_output_value;
|
||||
|
||||
// Indicate that the output buffer is valid for the next stage.
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WindowReset(struct WindowState* state) {
|
||||
memset(state->input, 0, state->size * sizeof(*state->input));
|
||||
memset(state->output, 0, state->size * sizeof(*state->output));
|
||||
state->input_used = 0;
|
||||
state->max_abs_output_value = 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kFrontendWindowBits 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct WindowState {
|
||||
size_t size;
|
||||
int16_t* coefficients;
|
||||
size_t step;
|
||||
|
||||
int16_t* input;
|
||||
size_t input_used;
|
||||
int16_t* output;
|
||||
int16_t max_abs_output_value;
|
||||
};
|
||||
|
||||
// Applies a window to the samples coming in, stepping forward at the given
|
||||
// rate.
|
||||
int WindowProcessSamples(struct WindowState* state, const int16_t* samples,
|
||||
size_t num_samples, size_t* num_samples_read);
|
||||
|
||||
void WindowReset(struct WindowState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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 "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Some platforms don't have M_PI
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
void WindowFillConfigWithDefaults(struct WindowConfig* config) {
|
||||
config->size_ms = 25;
|
||||
config->step_size_ms = 10;
|
||||
}
|
||||
|
||||
int WindowPopulateState(const struct WindowConfig* config,
|
||||
struct WindowState* state, int sample_rate) {
|
||||
state->size = config->size_ms * sample_rate / 1000;
|
||||
state->step = config->step_size_ms * sample_rate / 1000;
|
||||
|
||||
state->coefficients = malloc(state->size * sizeof(*state->coefficients));
|
||||
if (state->coefficients == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window coefficients\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Populate the window values.
|
||||
const float arg = M_PI * 2.0 / ((float)state->size);
|
||||
int i;
|
||||
for (i = 0; i < state->size; ++i) {
|
||||
float float_value = 0.5 - (0.5 * cos(arg * (i + 0.5)));
|
||||
// Scale it to fixed point and round it.
|
||||
state->coefficients[i] =
|
||||
floor(float_value * (1 << kFrontendWindowBits) + 0.5);
|
||||
}
|
||||
|
||||
state->input_used = 0;
|
||||
state->input = malloc(state->size * sizeof(*state->input));
|
||||
if (state->input == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window input\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->output = malloc(state->size * sizeof(*state->output));
|
||||
if (state->output == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window output\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WindowFreeStateContents(struct WindowState* state) {
|
||||
free(state->coefficients);
|
||||
free(state->input);
|
||||
free(state->output);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct WindowConfig {
|
||||
// length of window frame in milliseconds
|
||||
size_t size_ms;
|
||||
// length of step for next frame in milliseconds
|
||||
size_t step_size_ms;
|
||||
};
|
||||
|
||||
// Populates the WindowConfig with "sane" default values.
|
||||
void WindowFillConfigWithDefaults(struct WindowConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int WindowPopulateState(const struct WindowConfig* config,
|
||||
struct WindowState* state, int sample_rate);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void WindowFreeStateContents(struct WindowState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
|
||||
#ifndef TFLITE_DCHECK
|
||||
#define TFLITE_DCHECK(condition) (condition) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_EQ
|
||||
#define TFLITE_DCHECK_EQ(x, y) ((x) == (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_NE
|
||||
#define TFLITE_DCHECK_NE(x, y) ((x) != (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_GE
|
||||
#define TFLITE_DCHECK_GE(x, y) ((x) >= (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_GT
|
||||
#define TFLITE_DCHECK_GT(x, y) ((x) > (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_LE
|
||||
#define TFLITE_DCHECK_LE(x, y) ((x) <= (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DCHECK_LT
|
||||
#define TFLITE_DCHECK_LT(x, y) ((x) < (y)) ? (void)0 : TFLITE_ASSERT_FALSE
|
||||
#endif
|
||||
|
||||
// TODO(ahentz): Clean up: We should stick to the DCHECK versions.
|
||||
#ifndef TFLITE_CHECK
|
||||
#define TFLITE_CHECK(condition) (condition) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_EQ
|
||||
#define TFLITE_CHECK_EQ(x, y) ((x) == (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_NE
|
||||
#define TFLITE_CHECK_NE(x, y) ((x) != (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_GE
|
||||
#define TFLITE_CHECK_GE(x, y) ((x) >= (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_GT
|
||||
#define TFLITE_CHECK_GT(x, y) ((x) > (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_LE
|
||||
#define TFLITE_CHECK_LE(x, y) ((x) <= (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_CHECK_LT
|
||||
#define TFLITE_CHECK_LT(x, y) ((x) < (y)) ? (void)0 : TFLITE_ABORT
|
||||
#endif
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
// TODO(b/162019032): Consider removing these type-aliases.
|
||||
using int8 = std::int8_t;
|
||||
using uint8 = std::uint8_t;
|
||||
using int16 = std::int16_t;
|
||||
using uint16 = std::uint16_t;
|
||||
using int32 = std::int32_t;
|
||||
using uint32 = std::uint32_t;
|
||||
#endif // !defined(TF_LITE_STATIC_MEMORY)
|
||||
|
||||
// TFLITE_DEPRECATED()
|
||||
//
|
||||
// Duplicated from absl/base/macros.h to avoid pulling in that library.
|
||||
// Marks a deprecated class, struct, enum, function, method and variable
|
||||
// declarations. The macro argument is used as a custom diagnostic message (e.g.
|
||||
// suggestion of a better alternative).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// class TFLITE_DEPRECATED("Use Bar instead") Foo {...};
|
||||
// TFLITE_DEPRECATED("Use Baz instead") void Bar() {...}
|
||||
//
|
||||
// Every usage of a deprecated entity will trigger a warning when compiled with
|
||||
// clang's `-Wdeprecated-declarations` option. This option is turned off by
|
||||
// default, but the warnings will be reported by clang-tidy.
|
||||
#if defined(__clang__) && __cplusplus >= 201103L
|
||||
#define TFLITE_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#endif
|
||||
|
||||
#ifndef TFLITE_DEPRECATED
|
||||
#define TFLITE_DEPRECATED(message)
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_COMPATIBILITY_H_
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace tflite {
|
||||
|
||||
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
|
||||
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(__ZEPHYR__)
|
||||
#define TF_LITE_GLOBAL_STD_PREFIX
|
||||
#else
|
||||
#define TF_LITE_GLOBAL_STD_PREFIX std
|
||||
#endif
|
||||
|
||||
#define DECLARE_STD_GLOBAL_SWITCH1(tf_name, std_name) \
|
||||
template <class T> \
|
||||
inline T tf_name(const T x) { \
|
||||
return TF_LITE_GLOBAL_STD_PREFIX::std_name(x); \
|
||||
}
|
||||
|
||||
DECLARE_STD_GLOBAL_SWITCH1(TfLiteRound, round);
|
||||
DECLARE_STD_GLOBAL_SWITCH1(TfLiteExpm1, expm1);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_CPPMATH_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace tflite {
|
||||
|
||||
#if defined(TF_LITE_USE_GLOBAL_MAX) || defined(__ZEPHYR__)
|
||||
inline float TfLiteMax(const float& x, const float& y) {
|
||||
return std::max(x, y);
|
||||
}
|
||||
#else
|
||||
template <class T>
|
||||
inline T TfLiteMax(const T& x, const T& y) {
|
||||
return std::fmax(x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_MAX_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user