chore: import local project into Gitea

This commit is contained in:
2026-05-20 10:03:16 -07:00
commit 7bd88d29b5
4043 changed files with 1056916 additions and 0 deletions

View File

@@ -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.
==============================================================================*/

View File

@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "include" )

View File

@@ -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

View File

@@ -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 := .

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
)

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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_ */

View File

@@ -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);
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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"

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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};

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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