添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

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,487 @@
// 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 "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,599 @@
// 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 "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;
} lcd_obj_t;
typedef struct {
lcd_obj_t lcd;
intr_handle_t lcd_cam_intr_handle;
i2s_dev_t *i2s_dev;
} lcd_cam_obj_t;
typedef struct {
void (*i2s_write_data_func)(lcd_cam_obj_t *lcd_cam_obj, uint8_t *data, size_t len);
int rs_io_num;
lcd_cam_obj_t *lcd_cam_obj;
SemaphoreHandle_t mutex;
} i2s_lcd_driver_t;
static void IRAM_ATTR i2s_isr(void *arg)
{
BaseType_t HPTaskAwoken = pdFALSE;
lcd_cam_obj_t *lcd_cam_obj = (lcd_cam_obj_t *)arg;
i2s_dev_t *i2s_dev = lcd_cam_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(lcd_cam_obj->lcd.event_queue, (void *)&status.val, &HPTaskAwoken);
}
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
static void lcd_dma_set_int(lcd_cam_obj_t *lcd_cam_obj)
{
// Generate a data DMA linked list
for (int x = 0; x < lcd_cam_obj->lcd.dma_node_cnt; x++) {
lcd_cam_obj->lcd.dma[x].size = lcd_cam_obj->lcd.dma_node_buffer_size;
lcd_cam_obj->lcd.dma[x].length = lcd_cam_obj->lcd.dma_node_buffer_size;
lcd_cam_obj->lcd.dma[x].buf = (lcd_cam_obj->lcd.dma_buffer + lcd_cam_obj->lcd.dma_node_buffer_size * x);
lcd_cam_obj->lcd.dma[x].eof = !((x + 1) % lcd_cam_obj->lcd.dma_half_node_cnt);
lcd_cam_obj->lcd.dma[x].empty = (uint32_t)&lcd_cam_obj->lcd.dma[(x + 1) % lcd_cam_obj->lcd.dma_node_cnt];
}
lcd_cam_obj->lcd.dma[lcd_cam_obj->lcd.dma_half_node_cnt - 1].empty = (uint32_t)NULL;
lcd_cam_obj->lcd.dma[lcd_cam_obj->lcd.dma_node_cnt - 1].empty = (uint32_t)NULL;
}
static void lcd_dma_set_left(lcd_cam_obj_t *lcd_cam_obj, int pos, size_t len)
{
int end_pos = 0, size = 0;
// Processing data length is an integer multiple of lcd_cam_obj->lcd.dma_node_buffer_size
if (len % lcd_cam_obj->lcd.dma_node_buffer_size) {
end_pos = (pos % 2) * lcd_cam_obj->lcd.dma_half_node_cnt + len / lcd_cam_obj->lcd.dma_node_buffer_size;
size = len % lcd_cam_obj->lcd.dma_node_buffer_size;
} else {
end_pos = (pos % 2) * lcd_cam_obj->lcd.dma_half_node_cnt + len / lcd_cam_obj->lcd.dma_node_buffer_size - 1;
size = lcd_cam_obj->lcd.dma_node_buffer_size;
}
// Process the tail node to make it a DMA tail
lcd_cam_obj->lcd.dma[end_pos].size = size;
lcd_cam_obj->lcd.dma[end_pos].length = size;
lcd_cam_obj->lcd.dma[end_pos].eof = 1;
lcd_cam_obj->lcd.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(lcd_cam_obj_t *lcd_cam_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(lcd_cam_obj);
uint8_t fifo_mode = 1;
// Start signal
xQueueSend(lcd_cam_obj->lcd.event_queue, &event, 0);
cnt = len / lcd_cam_obj->lcd.dma_half_buffer_size;
// Process a complete piece of data, ping-pong operation
for (x = 0; x < cnt; x++) {
uint8_t *out = (uint8_t *)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
if (lcd_cam_obj->lcd.swap_data) {
for (y = 0; y < lcd_cam_obj->lcd.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 < lcd_cam_obj->lcd.dma_half_buffer_size; y += 4) {
out[y + 1] = in[(y >> 1) + 0];
out[y + 3] = in[(y >> 1) + 1];
}
}
data += lcd_cam_obj->lcd.dma_half_buffer_size >> 1;
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, fifo_mode, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, lcd_cam_obj->lcd.dma_half_buffer_size);
}
left = len % lcd_cam_obj->lcd.dma_half_buffer_size;
// Process remaining incomplete segment data
while (left) {
uint8_t *out = (uint8_t *)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
if (left > 2) {
cnt = left - left % 4;
left = left % 4;
data += cnt >> 1;
if (lcd_cam_obj->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];
}
lcd_dma_set_left(lcd_cam_obj, x, cnt);
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, fifo_mode, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, cnt);
x++;
}
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
}
static void i2s_write_16bit_data(lcd_cam_obj_t *lcd_cam_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(lcd_cam_obj);
uint8_t fifo_mode = 1;
// Start signal
xQueueSend(lcd_cam_obj->lcd.event_queue, &event, 0);
cnt = len / lcd_cam_obj->lcd.dma_half_buffer_size;
// Process a complete piece of data, ping-pong operation
for (x = 0; x < cnt; x++) {
uint8_t *out = (uint8_t *)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
if (lcd_cam_obj->lcd.swap_data) {
for (y = 0; y < lcd_cam_obj->lcd.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 < lcd_cam_obj->lcd.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 += lcd_cam_obj->lcd.dma_half_buffer_size;
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, fifo_mode, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, lcd_cam_obj->lcd.dma_half_buffer_size);
}
left = len % lcd_cam_obj->lcd.dma_half_buffer_size;
// Process remaining incomplete segment data
while (left) {
uint8_t *out = (uint8_t *)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
if (left > 2) {
cnt = left - left % 4;
left = left % 4;
data += cnt;
if (lcd_cam_obj->lcd.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 (lcd_cam_obj->lcd.swap_data) {
out[3] = in[0];
out[2] = in[1];
} else {
out[2] = in[0];
out[3] = in[1];
}
}
lcd_dma_set_left(lcd_cam_obj, x, cnt);
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, fifo_mode, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, cnt);
x++;
}
xQueueReceive(lcd_cam_obj->lcd.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(lcd_cam_obj_t *lcd_cam_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) {
lcd_cam_obj->lcd.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) % (lcd_cam_obj->lcd.dma_node_buffer_size * 2) == 0) {
break;
}
}
lcd_cam_obj->lcd.dma_buffer_size = max_dma_buffer_size - cnt;
} else {
lcd_cam_obj->lcd.dma_node_buffer_size = max_dma_buffer_size / 2;
lcd_cam_obj->lcd.dma_buffer_size = lcd_cam_obj->lcd.dma_node_buffer_size * 2;
}
lcd_cam_obj->lcd.dma_half_buffer_size = lcd_cam_obj->lcd.dma_buffer_size / 2;
lcd_cam_obj->lcd.dma_node_cnt = (lcd_cam_obj->lcd.dma_buffer_size) / lcd_cam_obj->lcd.dma_node_buffer_size; // Number of DMA nodes
lcd_cam_obj->lcd.dma_half_node_cnt = lcd_cam_obj->lcd.dma_node_cnt / 2;
ESP_LOGI(TAG, "lcd_buffer_size: %d, lcd_dma_size: %d, lcd_dma_node_cnt: %d", lcd_cam_obj->lcd.dma_buffer_size, lcd_cam_obj->lcd.dma_node_buffer_size, lcd_cam_obj->lcd.dma_node_cnt);
lcd_cam_obj->lcd.dma = (lldesc_t *)heap_caps_malloc(lcd_cam_obj->lcd.dma_node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
lcd_cam_obj->lcd.dma_buffer = (uint8_t *)heap_caps_malloc(lcd_cam_obj->lcd.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->lcd_cam_obj) {
return ESP_FAIL;
}
if (drv->lcd_cam_obj->lcd.event_queue) {
vQueueDelete(drv->lcd_cam_obj->lcd.event_queue);
}
if (drv->lcd_cam_obj->lcd.dma) {
heap_caps_free(drv->lcd_cam_obj->lcd.dma);
}
if (drv->lcd_cam_obj->lcd.dma_buffer) {
heap_caps_free(drv->lcd_cam_obj->lcd.dma_buffer);
}
if (drv->lcd_cam_obj->lcd_cam_intr_handle) {
esp_intr_free(drv->lcd_cam_obj->lcd_cam_intr_handle);
}
heap_caps_free(drv->lcd_cam_obj);
drv->lcd_cam_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;
lcd_cam_obj_t *lcd_cam_obj = (lcd_cam_obj_t *)heap_caps_calloc(1, sizeof(lcd_cam_obj_t), MALLOC_CAP_DMA);
if (lcd_cam_obj == NULL) {
ESP_LOGE(TAG, "lcd_cam object malloc failed");
return ESP_ERR_NO_MEM;
}
drv->lcd_cam_obj = lcd_cam_obj;
if (I2S_NUM_0 == config->i2s_port) {
lcd_cam_obj->i2s_dev = &I2S0;
periph_module_enable(PERIPH_I2S0_MODULE);
ESP_LOGI(TAG, "Enable I2S0");
} else if (I2S_NUM_1 == config->i2s_port) {
lcd_cam_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(lcd_cam_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(lcd_cam_obj, config->buffer_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "lcd config fail!");
break;
}
lcd_cam_obj->lcd.event_queue = xQueueCreate(1, sizeof(int));
lcd_cam_obj->lcd.width = config->data_width;
lcd_cam_obj->lcd.swap_data = config->swap_data;;
if (lcd_cam_obj->lcd.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, lcd_cam_obj, &lcd_cam_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, lcd_cam_obj, &lcd_cam_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->lcd_cam_obj, (uint8_t *)&data, 2);
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->lcd_cam_obj, (uint8_t *)&cmd, 2);
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->lcd_cam_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,481 @@
// 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 "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;
} lcd_obj_t;
typedef struct {
lcd_obj_t lcd;
intr_handle_t lcd_cam_intr_handle;
i2s_dev_t *i2s_dev;
} lcd_cam_obj_t;
typedef struct {
int rs_io_num;
lcd_cam_obj_t *lcd_cam_obj;
SemaphoreHandle_t mutex;
} i2s_lcd_driver_t;
static void IRAM_ATTR i2s_isr(void *arg)
{
BaseType_t HPTaskAwoken = pdFALSE;
lcd_cam_obj_t *lcd_cam_obj = (lcd_cam_obj_t*)arg;
i2s_dev_t *i2s_dev = lcd_cam_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(lcd_cam_obj->lcd.event_queue, (void*)&status.val, &HPTaskAwoken);
}
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
static void lcd_dma_set_int(lcd_cam_obj_t *lcd_cam_obj)
{
// Generate a data DMA linked list
for (int x = 0; x < lcd_cam_obj->lcd.dma_node_cnt; x++) {
lcd_cam_obj->lcd.dma[x].size = lcd_cam_obj->lcd.dma_node_buffer_size;
lcd_cam_obj->lcd.dma[x].length = lcd_cam_obj->lcd.dma_node_buffer_size;
lcd_cam_obj->lcd.dma[x].buf = (lcd_cam_obj->lcd.dma_buffer + lcd_cam_obj->lcd.dma_node_buffer_size * x);
lcd_cam_obj->lcd.dma[x].eof = !((x + 1) % lcd_cam_obj->lcd.dma_half_node_cnt);
lcd_cam_obj->lcd.dma[x].empty = (uint32_t)&lcd_cam_obj->lcd.dma[(x + 1) % lcd_cam_obj->lcd.dma_node_cnt];
}
lcd_cam_obj->lcd.dma[lcd_cam_obj->lcd.dma_half_node_cnt - 1].empty = (uint32_t)NULL;
lcd_cam_obj->lcd.dma[lcd_cam_obj->lcd.dma_node_cnt - 1].empty = (uint32_t)NULL;
}
static void lcd_dma_set_left(lcd_cam_obj_t *lcd_cam_obj, int pos, size_t len)
{
int end_pos = 0, size = 0;
// Processing data length is an integer multiple of lcd_cam_obj->lcd.dma_node_buffer_size
if (len % lcd_cam_obj->lcd.dma_node_buffer_size) {
end_pos = (pos % 2) * lcd_cam_obj->lcd.dma_half_node_cnt + len / lcd_cam_obj->lcd.dma_node_buffer_size;
size = len % lcd_cam_obj->lcd.dma_node_buffer_size;
} else {
end_pos = (pos % 2) * lcd_cam_obj->lcd.dma_half_node_cnt + len / lcd_cam_obj->lcd.dma_node_buffer_size - 1;
size = lcd_cam_obj->lcd.dma_node_buffer_size;
}
// Process the tail node to make it a DMA tail
lcd_cam_obj->lcd.dma[end_pos].size = size;
lcd_cam_obj->lcd.dma[end_pos].length = size;
lcd_cam_obj->lcd.dma[end_pos].eof = 1;
lcd_cam_obj->lcd.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_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(lcd_cam_obj_t *lcd_cam_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(lcd_cam_obj);
cnt = len / lcd_cam_obj->lcd.dma_half_buffer_size;
// Start signal
xQueueSend(lcd_cam_obj->lcd.event_queue, &event, 0);
// Process a complete piece of data, ping-pong operation
for (x = 0; x < cnt; x++) {
uint8_t *out = (uint8_t*)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
if (lcd_cam_obj->lcd.swap_data) {
for (y = 0; y < lcd_cam_obj->lcd.dma_half_buffer_size; y+=2) {
out[y+1] = in[y+0];
out[y+0] = in[y+1];
}
} else {
memcpy(out, in, lcd_cam_obj->lcd.dma_half_buffer_size);
}
data += lcd_cam_obj->lcd.dma_half_buffer_size;
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, lcd_cam_obj->lcd.dma_half_buffer_size);
}
left = len % lcd_cam_obj->lcd.dma_half_buffer_size;
// Process remaining incomplete segment data
if (left) {
uint8_t *out = (uint8_t*)lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt].buf;
uint8_t *in = data;
cnt = left - left % 2;
if (cnt) {
if (lcd_cam_obj->lcd.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(lcd_cam_obj, x, left);
xQueueReceive(lcd_cam_obj->lcd.event_queue, (void *)&event, portMAX_DELAY);
lcd_i2s_start(lcd_cam_obj->i2s_dev, ((uint32_t)&lcd_cam_obj->lcd.dma[(x % 2) * lcd_cam_obj->lcd.dma_half_node_cnt]) & 0xfffff, left);
}
xQueueReceive(lcd_cam_obj->lcd.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 = 0;
i2s_dev->clkm_conf.clk_sel = 2;
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->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->timing.val = 0;
i2s_dev->int_ena.val = 0;
i2s_dev->int_clr.val = ~0;
i2s_dev->conf2.lcd_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;
// Configuration data format
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->conf2.lcd_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 = 2;
i2s_dev->fifo_conf.tx_24msb_en = 0;
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(lcd_cam_obj_t *lcd_cam_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) {
lcd_cam_obj->lcd.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) % (lcd_cam_obj->lcd.dma_node_buffer_size * 2) == 0) {
break;
}
}
lcd_cam_obj->lcd.dma_buffer_size = max_dma_buffer_size - cnt;
} else {
lcd_cam_obj->lcd.dma_node_buffer_size = max_dma_buffer_size / 2;
lcd_cam_obj->lcd.dma_buffer_size = lcd_cam_obj->lcd.dma_node_buffer_size * 2;
}
lcd_cam_obj->lcd.dma_half_buffer_size = lcd_cam_obj->lcd.dma_buffer_size / 2;
lcd_cam_obj->lcd.dma_node_cnt = (lcd_cam_obj->lcd.dma_buffer_size) / lcd_cam_obj->lcd.dma_node_buffer_size; // Number of DMA nodes
lcd_cam_obj->lcd.dma_half_node_cnt = lcd_cam_obj->lcd.dma_node_cnt / 2;
ESP_LOGI(TAG, "lcd_buffer_size: %d, lcd_dma_size: %d, lcd_dma_node_cnt: %d", lcd_cam_obj->lcd.dma_buffer_size, lcd_cam_obj->lcd.dma_node_buffer_size, lcd_cam_obj->lcd.dma_node_cnt);
lcd_cam_obj->lcd.dma = (lldesc_t *)heap_caps_malloc(lcd_cam_obj->lcd.dma_node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
lcd_cam_obj->lcd.dma_buffer = (uint8_t *)heap_caps_malloc(lcd_cam_obj->lcd.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->lcd_cam_obj) {
return ESP_FAIL;
}
if (drv->lcd_cam_obj->lcd.event_queue) {
vQueueDelete(drv->lcd_cam_obj->lcd.event_queue);
}
if (drv->lcd_cam_obj->lcd.dma) {
heap_caps_free(drv->lcd_cam_obj->lcd.dma);
}
if (drv->lcd_cam_obj->lcd.dma_buffer) {
heap_caps_free(drv->lcd_cam_obj->lcd.dma_buffer);
}
if (drv->lcd_cam_obj->lcd_cam_intr_handle) {
esp_intr_free(drv->lcd_cam_obj->lcd_cam_intr_handle);
}
heap_caps_free(drv->lcd_cam_obj);
drv->lcd_cam_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;
lcd_cam_obj_t *lcd_cam_obj = (lcd_cam_obj_t *)heap_caps_calloc(1, sizeof(lcd_cam_obj_t), MALLOC_CAP_DMA);
if (lcd_cam_obj == NULL) {
ESP_LOGE(TAG, "lcd_cam object malloc error");
return ESP_ERR_NO_MEM;
}
drv->lcd_cam_obj = lcd_cam_obj;
if (I2S_NUM_0 == config->i2s_port) {
lcd_cam_obj->i2s_dev = &I2S0;
periph_module_enable(PERIPH_I2S0_MODULE);
} else {
ESP_LOGE(TAG, "Designated I2S peripheral not found");
}
ret |= i2s_lcd_reg_config(lcd_cam_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(lcd_cam_obj, config->buffer_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "lcd config fail!");
lcd_cam_deinit(drv);
return ESP_FAIL;
}
lcd_cam_obj->lcd.event_queue = xQueueCreate(1, sizeof(int));
lcd_cam_obj->lcd.width = config->data_width;
lcd_cam_obj->lcd.swap_data = config->swap_data;
if (lcd_cam_obj->lcd.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, lcd_cam_obj, &lcd_cam_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->lcd_cam_obj, (uint8_t *)&data, 2);
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->lcd_cam_obj, (uint8_t *)&cmd, 2);
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->lcd_cam_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,253 @@
// 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 "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,
};
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);
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;
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);
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,3 @@
idf_component_register(SRCS "test_i2c_bus.c" "test_spi_bus.c"
INCLUDE_DIRS .
REQUIRES test_utils bus)

View File

@@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View File

@@ -0,0 +1,269 @@
// 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 "unity.h"
#include "test_utils.h"
#include "unity_config.h"
#include "i2c_bus.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define I2C_MASTER_SCL_IO (gpio_num_t)22 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (gpio_num_t)21 /*!< gpio number for I2C master data */
#define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/
#define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
#define DELAY_TIME_BETWEEN_ITEMS_MS 1234 /*!< delay time between different test items */
#define I2C_SLAVE_SCL_IO 17 /*!<gpio number for i2c slave clock */
#define I2C_SLAVE_SDA_IO 16 /*!<gpio number for i2c slave data */
#define I2C_MASTER_NUM I2C_NUM_1 /*!<I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define I2C_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
#define I2C_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
#define I2C_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
void i2c_bus_init_deinit_test()
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
/** configs not change**/
i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
/** configs not change**/
conf.master.clk_speed *= 2;
i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
vTaskDelay(100 / portTICK_RATE_MS);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
TEST_ASSERT(i2c0_bus_1 == NULL);
}
void i2c_bus_device_add_test()
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus_1, 0x01, 400000);
TEST_ASSERT(i2c_device1 != NULL);
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c0_bus_1, 0x01, 100000);
TEST_ASSERT(i2c_device2 != NULL);
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
i2c_bus_device_delete(&i2c_device2);
TEST_ASSERT(i2c_device2 == NULL);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
TEST_ASSERT(i2c0_bus_1 == NULL);
}
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2)
// print the reading buffer
static void disp_buf(uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
printf("%02x ", buf[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
}
static void i2c_master_write_test(void)
{
uint8_t *data_wr = (uint8_t *) malloc(DATA_LENGTH);
int i;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
TEST_ASSERT(i2c0_bus != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
TEST_ASSERT(i2c_device1 != NULL);
unity_wait_for_signal("i2c slave init finish");
unity_send_signal("master write");
for (i = 0; i < DATA_LENGTH / 2; i++) {
data_wr[i] = i;
}
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, DATA_LENGTH / 2, data_wr);
disp_buf(data_wr, i + 1);
free(data_wr);
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus));
TEST_ASSERT(i2c0_bus == NULL);
}
static void i2c_slave_read_test(void)
{
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
int size_rd = 0;
int len = 0;
i2c_config_t conf_slave = {
.mode = I2C_MODE_SLAVE,
.sda_io_num = I2C_SLAVE_SDA_IO,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.slave.addr_10bit_en = 0,
.slave.slave_addr = ESP_SLAVE_ADDR,
};
TEST_ESP_OK(i2c_param_config(I2C_SLAVE_NUM, &conf_slave));
TEST_ESP_OK(i2c_driver_install(I2C_SLAVE_NUM, I2C_MODE_SLAVE,
I2C_SLAVE_RX_BUF_LEN,
I2C_SLAVE_TX_BUF_LEN, 0));
unity_send_signal("i2c slave init finish");
unity_wait_for_signal("master write");
while (1) {
len = i2c_slave_read_buffer(I2C_SLAVE_NUM, data_rd + size_rd, DATA_LENGTH, 10000 / portTICK_RATE_MS);
if (len == 0) {
break;
}
size_rd += len;
}
disp_buf(data_rd, size_rd);
for (int i = 0; i < size_rd; i++) {
TEST_ASSERT(data_rd[i] == i);
}
free(data_rd);
unity_send_signal("ready to delete");
TEST_ESP_OK(i2c_driver_delete(I2C_SLAVE_NUM));
}
TEST_CASE_MULTIPLE_DEVICES("I2C master write slave test", "[i2c_bus]", i2c_master_write_test, i2c_slave_read_test);
static void master_read_slave_test(void)
{
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
memset(data_rd, 0, DATA_LENGTH);
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
TEST_ASSERT(i2c0_bus != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
TEST_ASSERT(i2c_device1 != NULL);
unity_wait_for_signal("i2c slave init finish");
unity_send_signal("slave write");
unity_wait_for_signal("master read");
i2c_bus_read_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_rd);
vTaskDelay(100 / portTICK_RATE_MS);
for (int i = 0; i < RW_TEST_LENGTH; i++) {
printf("%d\n", data_rd[i]);
TEST_ASSERT(data_rd[i] == i);
}
free(data_rd);
unity_send_signal("ready to delete");
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus));
TEST_ASSERT(i2c0_bus == NULL);
}
static void slave_write_buffer_test(void)
{
uint8_t *data_wr = (uint8_t *) malloc(DATA_LENGTH);
int size_rd;
i2c_config_t conf_slave = {
.mode = I2C_MODE_SLAVE,
.sda_io_num = I2C_SLAVE_SDA_IO,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.slave.addr_10bit_en = 0,
.slave.slave_addr = ESP_SLAVE_ADDR,
};
TEST_ESP_OK(i2c_param_config(I2C_SLAVE_NUM, &conf_slave));
TEST_ESP_OK(i2c_driver_install(I2C_SLAVE_NUM, I2C_MODE_SLAVE,
I2C_SLAVE_RX_BUF_LEN,
I2C_SLAVE_TX_BUF_LEN, 0));
unity_send_signal("i2c slave init finish");
unity_wait_for_signal("slave write");
for (int i = 0; i < DATA_LENGTH / 2; i++) {
data_wr[i] = i;
}
size_rd = i2c_slave_write_buffer(I2C_SLAVE_NUM, data_wr, RW_TEST_LENGTH, 2000 / portTICK_RATE_MS);
disp_buf(data_wr, size_rd);
unity_send_signal("master read");
unity_wait_for_signal("ready to delete");
free(data_wr);
i2c_driver_delete(I2C_SLAVE_NUM);
}
TEST_CASE_MULTIPLE_DEVICES("I2C master read slave test", "[i2c_bus]", master_read_slave_test, slave_write_buffer_test);
#endif //DISABLED_FOR_TARGET(ESP32S2)
TEST_CASE("i2c bus init-deinit test", "[bus][i2c_bus]")
{
i2c_bus_init_deinit_test();
i2c_bus_device_add_test();
}

View File

@@ -0,0 +1,119 @@
// 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 "unity.h"
#include "unity_config.h"
#include "spi_bus.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define SPI_SCK_IO 18
#define SPI_MOSI_IO 23
#define SPI_MISO_IO 19
void spi_bus_init_deinit_test()
{
spi_bus_handle_t bus_handle = NULL;
spi_config_t bus_conf = {
.miso_io_num = SPI_MISO_IO,
.mosi_io_num = SPI_MOSI_IO,
.sclk_io_num = SPI_SCK_IO,
};
bus_handle = spi_bus_create(SPI2_HOST, &bus_conf);
TEST_ASSERT(bus_handle != NULL);
TEST_ASSERT(ESP_OK == spi_bus_delete(&bus_handle));
TEST_ASSERT(bus_handle == NULL);
bus_handle = spi_bus_create(SPI3_HOST, &bus_conf);
TEST_ASSERT(bus_handle != NULL);
TEST_ASSERT(ESP_OK == spi_bus_delete(&bus_handle));
TEST_ASSERT(bus_handle == NULL);
}
/* connect mosi with miso for transfer test */
void spi_bus_transfer_test()
{
spi_bus_handle_t bus_handle = NULL;
spi_config_t bus_conf = {
.miso_io_num = SPI_MISO_IO,
.mosi_io_num = SPI_MOSI_IO,
.sclk_io_num = SPI_SCK_IO,
};
spi_device_config_t device_conf = {
.cs_io_num = NULL_SPI_CS_PIN,
.mode = 0,
.clock_speed_hz = 20 * 1000 * 1000,
};
bus_handle = spi_bus_create(SPI2_HOST, &bus_conf);
TEST_ASSERT(bus_handle != NULL);
spi_bus_device_handle_t device_handle = NULL;
device_handle = spi_bus_device_create(bus_handle, &device_conf);
TEST_ASSERT(device_handle != NULL);
printf("************byte transfer test***************\n");
for (uint8_t i = 0; i < 200; i++) {
uint8_t in = 0;
TEST_ASSERT(ESP_OK == spi_bus_transfer_byte(device_handle, i, &in));
TEST_ASSERT_EQUAL_UINT8(i, in);
printf("in=%u\n", in);
}
vTaskDelay(2);
printf("************bytes transfer test***************\n");
uint8_t data[200] = {0};
uint8_t data_in[200] = {0};
for (uint8_t i = 0; i < 200; i++) {
data[i] = i;
}
TEST_ASSERT(ESP_OK == spi_bus_transfer_bytes(device_handle, data, data_in, 200));
for (uint8_t i = 0; i < 200; i++) {
printf("%u ", data_in[i]);
}
printf("\n");
vTaskDelay(2);
printf("************reg16 transfer test***************\n");
for (uint16_t i = (0xffff - 200); i < 0xffff; i++) {
uint16_t in = 0;
TEST_ASSERT(ESP_OK == spi_bus_transfer_reg16(device_handle, i, &in));
TEST_ASSERT_EQUAL_UINT16(i, in);
printf("in=%u\n", in);
}
vTaskDelay(2);
printf("************reg32 transfer test***************\n");
for (uint32_t i = (0xffffffff - 200); i < 0xffffffff; i++) {
uint32_t in = 0;
TEST_ASSERT(ESP_OK == spi_bus_transfer_reg32(device_handle, i, &in));
TEST_ASSERT_EQUAL_UINT32(i, in);
printf("in=%x\n", in);
}
TEST_ASSERT(ESP_OK == spi_bus_device_delete(&device_handle));
TEST_ASSERT(device_handle == NULL);
TEST_ASSERT(ESP_OK == spi_bus_delete(&bus_handle));
TEST_ASSERT(bus_handle == NULL);
}
TEST_CASE("spi bus init-deinit test", "[bus]spi_bus]")
{
spi_bus_init_deinit_test();
}
TEST_CASE("spi bus transfer test", "[bus][spi_bus]")
{
spi_bus_transfer_test();
}

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "button_adc.c" "button_gpio.c" "iot_button.c"
INCLUDE_DIRS include
PRIV_REQUIRES esp_adc_cal)

View File

@@ -0,0 +1,46 @@
menu "IoT Button"
config BUTTON_PERIOD_TIME_MS
int "BUTTON PERIOD TIME (MS)"
range 2 20
default 5
help
"Button scan interval"
config BUTTON_DEBOUNCE_TICKS
int "BUTTON DEBOUNCE TICKS"
range 1 8
default 2
config BUTTON_SHORT_PRESS_TIME_MS
int "BUTTON SHORT PRESS TIME (MS)"
range 50 800
default 180
config BUTTON_LONG_PRESS_TIME_MS
int "BUTTON LONG PRESS TIME (MS)"
range 500 5000
default 1500
config ADC_BUTTON_MAX_CHANNEL
int "ADC BUTTON MAX CHANNEL"
range 1 5
default 3
help
"Maximum number of channels for ADC buttons"
config ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
int "ADC BUTTON MAX BUTTON PER CHANNEL"
range 1 10
default 8
help
"Maximum number of buttons per channel"
config ADC_BUTTON_SAMPLE_TIMES
int "ADC BUTTON SAMPLE TIMES"
range 1 4
default 1
help
"Number of samples per scan"
endmenu

View File

@@ -0,0 +1,208 @@
// 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 <string.h>
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "button_adc.h"
#include "esp_timer.h"
static const char *TAG = "adc button";
#define ADC_BTN_CHECK(a, str, ret_val) \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
#define DEFAULT_VREF 1100
#define NO_OF_SAMPLES CONFIG_ADC_BUTTON_SAMPLE_TIMES //Multisampling
#if CONFIG_IDF_TARGET_ESP32
#define ADC_BUTTON_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_BUTTON_WIDTH ADC_WIDTH_BIT_13
#endif
#define ADC_BUTTON_ATTEN ADC_ATTEN_DB_11
#define ADC_BUTTON_ADC_UNIT ADC_UNIT_1
#define ADC_BUTTON_MAX_CHANNEL CONFIG_ADC_BUTTON_MAX_CHANNEL
#define ADC_BUTTON_MAX_BUTTON CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
typedef struct {
uint16_t min;
uint16_t max;
} button_data_t;
typedef struct {
adc1_channel_t channel;
uint8_t is_init;
button_data_t btns[ADC_BUTTON_MAX_BUTTON]; /* all button on the channel */
uint64_t last_time; /* the last time of adc sample */
} btn_adc_channel_t;
typedef struct {
bool is_configured;
esp_adc_cal_characteristics_t adc_chars;
btn_adc_channel_t ch[ADC_BUTTON_MAX_CHANNEL];
uint8_t ch_num;
} adc_button_t;
static adc_button_t g_button = {0};
static int find_unused_channel(void)
{
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
if (0 == g_button.ch[i].is_init) {
return i;
}
}
return -1;
}
static int find_channel(adc1_channel_t channel)
{
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
if (channel == g_button.ch[i].channel) {
return i;
}
}
return -1;
}
esp_err_t button_adc_init(const button_adc_config_t *config)
{
ADC_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
ADC_BTN_CHECK(config->adc_channel < ADC1_CHANNEL_MAX, "channel out of range", ESP_ERR_NOT_SUPPORTED);
ADC_BTN_CHECK(config->button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_NOT_SUPPORTED);
ADC_BTN_CHECK(config->max > 0, "key max voltage invalid", ESP_ERR_INVALID_ARG);
int ch_index = find_channel(config->adc_channel);
if (ch_index >= 0) { /**< the channel has been initialized */
ADC_BTN_CHECK(g_button.ch[ch_index].btns[config->button_index].max == 0, "The button_index has been used", ESP_ERR_INVALID_STATE);
} else { /**< this is a new channel */
int unused_ch_index = find_unused_channel();
ADC_BTN_CHECK(unused_ch_index >= 0, "exceed max channel number, can't create a new channel", ESP_ERR_INVALID_STATE);
ch_index = unused_ch_index;
}
/** initialize adc */
if (0 == g_button.is_configured) {
//Configure ADC
adc1_config_width(ADC_BUTTON_WIDTH);
//Characterize ADC
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_BUTTON_ADC_UNIT, ADC_BUTTON_ATTEN, ADC_BUTTON_WIDTH, DEFAULT_VREF, &g_button.adc_chars);
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
ESP_LOGI(TAG, "Characterized using Two Point Value");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
ESP_LOGI(TAG, "Characterized using eFuse Vref");
} else {
ESP_LOGI(TAG, "Characterized using Default Vref");
}
g_button.is_configured = 1;
}
/** initialize adc channel */
if (0 == g_button.ch[ch_index].is_init) {
adc1_config_channel_atten(config->adc_channel, ADC_BUTTON_ATTEN);
g_button.ch[ch_index].channel = config->adc_channel;
g_button.ch[ch_index].is_init = 1;
g_button.ch[ch_index].last_time = 0;
}
g_button.ch[ch_index].btns[config->button_index].max = config->max;
g_button.ch[ch_index].btns[config->button_index].min = config->min;
g_button.ch_num++;
return ESP_OK;
}
esp_err_t button_adc_deinit(adc1_channel_t channel, int button_index)
{
ADC_BTN_CHECK(channel < ADC1_CHANNEL_MAX, "channel out of range", ESP_ERR_INVALID_ARG);
ADC_BTN_CHECK(button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_INVALID_ARG);
int ch_index = find_channel(channel);
ADC_BTN_CHECK(ch_index >= 0, "can't find the channel", ESP_ERR_INVALID_ARG);
g_button.ch[ch_index].btns[button_index].max = 0;
g_button.ch[ch_index].btns[button_index].min = 0;
/** check button usage on the channel*/
uint8_t unused_button = 0;
for (size_t i = 0; i < ADC_BUTTON_MAX_BUTTON; i++) {
if (0 == g_button.ch[ch_index].btns[i].max) {
unused_button++;
}
}
if (unused_button == ADC_BUTTON_MAX_BUTTON && g_button.ch[ch_index].is_init) { /**< if all button is unused, deinit the channel */
/* TODO: to deinit the channel */
g_button.ch[ch_index].is_init = 0;
g_button.ch[ch_index].channel = ADC1_CHANNEL_MAX;
ESP_LOGD(TAG, "all button is unused on channel%d, deinit the channel", g_button.ch[ch_index].channel);
}
/** check channel usage on the adc*/
uint8_t unused_ch = 0;
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
if (0 == g_button.ch[i].is_init) {
unused_ch++;
}
}
if (unused_ch == ADC_BUTTON_MAX_CHANNEL && g_button.is_configured) { /**< if all channel is unused, deinit the adc */
/* TODO: to deinit the peripheral adc */
g_button.is_configured = false;
memset(&g_button, 0, sizeof(adc_button_t));
ESP_LOGD(TAG, "all channel is unused, , deinit adc");
}
return ESP_OK;
}
static uint32_t get_adc_volatge(adc1_channel_t channel)
{
uint32_t adc_reading = 0;
//Multisampling
for (int i = 0; i < NO_OF_SAMPLES; i++) {
adc_reading += adc1_get_raw(channel);
}
adc_reading /= NO_OF_SAMPLES;
//Convert adc_reading to voltage in mV
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, &g_button.adc_chars);
ESP_LOGV(TAG, "Raw: %d\tVoltage: %dmV", adc_reading, voltage);
return voltage;
}
uint8_t button_adc_get_key_level(void *button_index)
{
static uint16_t vol = 0;
uint32_t ch = ADC_BUTTON_SPLIT_CHANNEL(button_index);
uint32_t index = ADC_BUTTON_SPLIT_INDEX(button_index);
ADC_BTN_CHECK(ch < ADC1_CHANNEL_MAX, "channel out of range", 0);
ADC_BTN_CHECK(index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", 0);
int ch_index = find_channel(ch);
ADC_BTN_CHECK(ch_index >= 0, "The button_index is not init", 0);
/** It starts only when the elapsed time is more than 1ms */
if ((esp_timer_get_time() - g_button.ch[ch_index].last_time) > 1000) {
vol = get_adc_volatge(ch);
g_button.ch[ch_index].last_time = esp_timer_get_time();
}
if (vol <= g_button.ch[ch_index].btns[index].max &&
vol > g_button.ch[ch_index].btns[index].min) {
return 1;
}
return 0;
}

View File

@@ -0,0 +1,65 @@
// 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 "esp_log.h"
#include "driver/gpio.h"
#include "button_gpio.h"
static const char *TAG = "gpio button";
#define GPIO_BTN_CHECK(a, str, ret_val) \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
esp_err_t button_gpio_init(const button_gpio_config_t *config)
{
GPIO_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
gpio_config_t gpio_conf;
gpio_conf.intr_type = GPIO_INTR_DISABLE;
gpio_conf.mode = GPIO_MODE_INPUT;
gpio_conf.pin_bit_mask = (1ULL << config->gpio_num);
if (config->active_level) {
gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
} else {
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
}
gpio_config(&gpio_conf);
return ESP_OK;
}
esp_err_t button_gpio_deinit(int gpio_num)
{
/** both disable pullup and pulldown */
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << gpio_num),
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
};
gpio_config(&gpio_conf);
return ESP_OK;
}
uint8_t button_gpio_get_key_level(void *gpio_num)
{
return (uint8_t)gpio_get_level((uint32_t)gpio_num);
}

View File

@@ -0,0 +1,8 @@
#
# "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,79 @@
// 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_BUTTON_ADC_H_
#define _IOT_BUTTON_ADC_H_
#include "driver/gpio.h"
#include "driver/adc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ADC_BUTTON_COMBINE(channel, index) ((channel)<<8 | (index))
#define ADC_BUTTON_SPLIT_INDEX(data) ((uint32_t)(data)&0xff)
#define ADC_BUTTON_SPLIT_CHANNEL(data) (((uint32_t)(data) >> 8) & 0xff)
/**
* @brief adc button configuration
*
*/
typedef struct {
adc1_channel_t adc_channel; /**< Channel of ADC */
uint8_t button_index; /**< button index on the channel */
uint16_t min; /**< min voltage in mv corresponding to the button */
uint16_t max; /**< max voltage in mv corresponding to the button */
} button_adc_config_t;
/**
* @brief Initialize gpio button
*
* @param config pointer of configuration struct
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG Arguments is NULL.
* - ESP_ERR_NOT_SUPPORTED Arguments out of range.
* - ESP_ERR_INVALID_STATE State is error.
*/
esp_err_t button_adc_init(const button_adc_config_t *config);
/**
* @brief Deinitialize gpio button
*
* @param channel ADC channel
* @param button_index Button index on the channel
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG Arguments is invalid.
*/
esp_err_t button_adc_deinit(adc1_channel_t channel, int button_index);
/**
* @brief Get the adc button level
*
* @param button_index It is compressed by ADC channel and button index, use the macro ADC_BUTTON_COMBINE to generate. It will be treated as a uint32_t variable.
*
* @return
* - 0 Not pressed
* - 1 Pressed
*/
uint8_t button_adc_get_key_level(void *button_index);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,65 @@
// 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_BUTTON_GPIO_H_
#define _IOT_BUTTON_GPIO_H_
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief gpio button configuration
*
*/
typedef struct {
int32_t gpio_num;
uint8_t active_level;
} button_gpio_config_t;
/**
* @brief Initialize gpio button
*
* @param config pointer of configuration struct
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG Arguments is NULL.
*/
esp_err_t button_gpio_init(const button_gpio_config_t *config);
/**
* @brief Deinitialize gpio button
*
* @param gpio_num gpio number of button
*
* @return Always return ESP_OK
*/
esp_err_t button_gpio_deinit(int gpio_num);
/**
* @brief Get current level on button gpio
*
* @param gpio_num gpio number of button, it will be treated as a uint32_t variable.
*
* @return Level on gpio
*/
uint8_t button_gpio_get_key_level(void *gpio_num);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,131 @@
// 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_BUTTON_H_
#define _IOT_BUTTON_H_
#include "button_adc.h"
#include "button_gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (* button_cb_t)(void *);
typedef void *button_handle_t;
/**
* @brief Button events
*
*/
typedef enum {
BUTTON_PRESS_DOWN = 0,
BUTTON_PRESS_UP,
BUTTON_PRESS_REPEAT,
BUTTON_SINGLE_CLICK,
BUTTON_DOUBLE_CLICK,
BUTTON_LONG_PRESS_START,
BUTTON_LONG_PRESS_HOLD,
BUTTON_EVENT_MAX,
BUTTON_NONE_PRESS,
} button_event_t;
/**
* @brief Supported button type
*
*/
typedef enum {
BUTTON_TYPE_GPIO,
BUTTON_TYPE_ADC,
} button_type_t;
/**
* @brief Button configuration
*
*/
typedef struct {
button_type_t type; /**< button type, The corresponding button configuration must be filled */
union {
button_gpio_config_t gpio_button_config; /**< gpio button configuration */
button_adc_config_t adc_button_config; /**< adc button configuration */
}; /**< button configuration */
} button_config_t;
/**
* @brief Create a button
*
* @param config pointer of button configuration, must corresponding the button type
*
* @return A handle to the created button, or NULL in case of error.
*/
button_handle_t iot_button_create(const button_config_t *config);
/**
* @brief Delete a button
*
* @param btn_handle A button handle to delete
*
* @return
* - ESP_OK Success
* - ESP_FAIL Failure
*/
esp_err_t iot_button_delete(button_handle_t btn_handle);
/**
* @brief Register the button event callback function.
*
* @param btn_handle A button handle to register
* @param event Button event
* @param cb Callback function.
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG Arguments is invalid.
*/
esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb);
/**
* @brief Unregister the button event callback function.
*
* @param btn_handle A button handle to unregister
* @param event Button event
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG Arguments is invalid.
*/
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event);
/**
* @brief Get button event
*
* @param btn_handle Button handle
*
* @return Current button event. See button_event_t
*/
button_event_t iot_button_get_event(button_handle_t btn_handle);
/**
* @brief Get button repeat times
*
* @param btn_handle Button handle
*
* @return button pressed times. For example, double-click return 2, triple-click return 3, etc.
*/
uint8_t iot_button_get_repeat(button_handle_t btn_handle);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,306 @@
// 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 "freertos/timers.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "iot_button.h"
#include "esp_timer.h"
#include "sdkconfig.h"
static const char *TAG = "button";
#define BTN_CHECK(a, str, ret_val) \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
typedef struct Button {
uint16_t ticks;
uint8_t repeat;
button_event_t event;
uint8_t state: 3;
uint8_t debounce_cnt: 3;
uint8_t active_level: 1;
uint8_t button_level: 1;
uint8_t (*hal_button_Level)(void *usr_data);
void *usr_data;
button_type_t type;
button_cb_t cb[BUTTON_EVENT_MAX];
struct Button *next;
} button_dev_t;
//button handle list head.
static button_dev_t *g_head_handle = NULL;
static esp_timer_handle_t g_button_timer_handle;
static bool g_is_timer_running = false;
#define TICKS_INTERVAL CONFIG_BUTTON_PERIOD_TIME_MS
#define DEBOUNCE_TICKS CONFIG_BUTTON_DEBOUNCE_TICKS //MAX 8
#define SHORT_TICKS (CONFIG_BUTTON_SHORT_PRESS_TIME_MS /TICKS_INTERVAL)
#define LONG_TICKS (CONFIG_BUTTON_LONG_PRESS_TIME_MS /TICKS_INTERVAL)
#define CALL_EVENT_CB(ev) if(btn->cb[ev])btn->cb[ev](btn)
/**
* @brief Button driver core function, driver state machine.
*/
static void button_handler(button_dev_t *btn)
{
uint8_t read_gpio_level = btn->hal_button_Level(btn->usr_data);
/** ticks counter working.. */
if ((btn->state) > 0) {
btn->ticks++;
}
/**< button debounce handle */
if (read_gpio_level != btn->button_level) {
if (++(btn->debounce_cnt) >= DEBOUNCE_TICKS) {
btn->button_level = read_gpio_level;
btn->debounce_cnt = 0;
}
} else {
btn->debounce_cnt = 0;
}
/** State machine */
switch (btn->state) {
case 0:
if (btn->button_level == btn->active_level) {
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
btn->ticks = 0;
btn->repeat = 1;
btn->state = 1;
} else {
btn->event = (uint8_t)BUTTON_NONE_PRESS;
}
break;
case 1:
if (btn->button_level != btn->active_level) {
btn->event = (uint8_t)BUTTON_PRESS_UP;
CALL_EVENT_CB(BUTTON_PRESS_UP);
btn->ticks = 0;
btn->state = 2;
} else if (btn->ticks > LONG_TICKS) {
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
CALL_EVENT_CB(BUTTON_LONG_PRESS_START);
btn->state = 5;
}
break;
case 2:
if (btn->button_level == btn->active_level) {
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
btn->repeat++;
// CALL_EVENT_CB(BUTTON_PRESS_REPEAT); // repeat hit
btn->ticks = 0;
btn->state = 3;
} else if (btn->ticks > SHORT_TICKS) {
if (btn->repeat == 1) {
btn->event = (uint8_t)BUTTON_SINGLE_CLICK;
CALL_EVENT_CB(BUTTON_SINGLE_CLICK);
}
// else if (btn->repeat == 2) {
// btn->event = (uint8_t)BUTTON_DOUBLE_CLICK;
// CALL_EVENT_CB(BUTTON_DOUBLE_CLICK); // repeat hit
// ESP_LOGE(TAG, "003double click");
// }
else {
CALL_EVENT_CB(BUTTON_PRESS_REPEAT); // repeat hit
}
btn->state = 0;
}
break;
case 3:
if (btn->button_level != btn->active_level) {
btn->event = (uint8_t)BUTTON_PRESS_UP;
CALL_EVENT_CB(BUTTON_PRESS_UP);
if (btn->ticks < SHORT_TICKS) {
btn->ticks = 0;
btn->state = 2; //repeat press
} else {
btn->state = 0;
}
}
break;
case 5:
if (btn->button_level == btn->active_level) {
//continue hold trigger
btn->event = (uint8_t)BUTTON_LONG_PRESS_HOLD;
CALL_EVENT_CB(BUTTON_LONG_PRESS_HOLD);
} else { //releasd
btn->event = (uint8_t)BUTTON_PRESS_UP;
CALL_EVENT_CB(BUTTON_PRESS_UP);
btn->state = 0; //reset
}
break;
}
}
static void button_cb(void *args)
{
button_dev_t *target;
for (target = g_head_handle; target; target = target->next) {
button_handler(target);
}
}
static button_dev_t *button_create_com(uint8_t active_level, uint8_t (*hal_get_key_state)(void *usr_data), void *usr_data)
{
BTN_CHECK(NULL != hal_get_key_state, "Function pointer is invalid", NULL);
button_dev_t *btn = (button_dev_t *) calloc(1, sizeof(button_dev_t));
BTN_CHECK(NULL != btn, "Button memory alloc failed", NULL);
btn->usr_data = usr_data;
btn->event = BUTTON_NONE_PRESS;
btn->active_level = active_level;
btn->hal_button_Level = hal_get_key_state;
btn->button_level = !active_level;
/** Add handle to list */
btn->next = g_head_handle;
g_head_handle = btn;
if (false == g_is_timer_running) {
esp_timer_create_args_t button_timer;
button_timer.arg = NULL;
button_timer.callback = button_cb;
button_timer.dispatch_method = ESP_TIMER_TASK;
button_timer.name = "button_timer";
esp_timer_create(&button_timer, &g_button_timer_handle);
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
g_is_timer_running = true;
}
return btn;
}
static esp_err_t button_delete_com(button_dev_t *btn)
{
BTN_CHECK(NULL != btn, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
button_dev_t **curr;
for (curr = &g_head_handle; *curr; ) {
button_dev_t *entry = *curr;
if (entry == btn) {
*curr = entry->next;
free(entry);
} else {
curr = &entry->next;
}
}
/* count button number */
uint16_t number = 0;
button_dev_t *target = g_head_handle;
while (target) {
target = target->next;
number++;
}
ESP_LOGD(TAG, "remain btn number=%d", number);
if (0 == number && g_is_timer_running) { /**< if all button is deleted, stop the timer */
esp_timer_stop(g_button_timer_handle);
esp_timer_delete(g_button_timer_handle);
g_is_timer_running = false;
}
return ESP_OK;
}
button_handle_t iot_button_create(const button_config_t *config)
{
esp_err_t ret = ESP_OK;
button_dev_t *btn = NULL;
switch (config->type) {
case BUTTON_TYPE_GPIO: {
const button_gpio_config_t *cfg = &(config->gpio_button_config);
ret = button_gpio_init(cfg);
BTN_CHECK(ESP_OK == ret, "gpio button init failed", NULL);
btn = button_create_com(cfg->active_level, button_gpio_get_key_level, (void *)cfg->gpio_num);
} break;
case BUTTON_TYPE_ADC: {
const button_adc_config_t *cfg = &(config->adc_button_config);
ret = button_adc_init(cfg);
BTN_CHECK(ESP_OK == ret, "adc button init failed", NULL);
btn = button_create_com(1, button_adc_get_key_level, (void *)ADC_BUTTON_COMBINE(cfg->adc_channel, cfg->button_index));
} break;
default:
ESP_LOGE(TAG, "Unsupported button type");
break;
}
BTN_CHECK(NULL != btn, "button create failed", NULL);
btn->type = config->type;
return (button_handle_t)btn;
}
esp_err_t iot_button_delete(button_handle_t btn_handle)
{
esp_err_t ret = ESP_OK;
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
button_dev_t *btn = (button_dev_t *)btn_handle;
switch (btn->type) {
case BUTTON_TYPE_GPIO:
ret = button_gpio_deinit((int)(btn->usr_data));
break;
case BUTTON_TYPE_ADC:
ret = button_adc_deinit(ADC_BUTTON_SPLIT_CHANNEL(btn->usr_data), ADC_BUTTON_SPLIT_INDEX(btn->usr_data));
break;
default:
break;
}
BTN_CHECK(ESP_OK == ret, "button deinit failed", ESP_FAIL);
button_delete_com(btn);
return ESP_OK;
}
esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb)
{
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
button_dev_t *btn = (button_dev_t *) btn_handle;
btn->cb[event] = cb;
return ESP_OK;
}
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)
{
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
button_dev_t *btn = (button_dev_t *) btn_handle;
btn->cb[event] = NULL;
return ESP_OK;
}
button_event_t iot_button_get_event(button_handle_t btn_handle)
{
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", BUTTON_NONE_PRESS);
button_dev_t *btn = (button_dev_t *) btn_handle;
return btn->event;
}
uint8_t iot_button_get_repeat(button_handle_t btn_handle)
{
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
button_dev_t *btn = (button_dev_t *) btn_handle;
return btn->repeat;
}

View File

@@ -0,0 +1,3 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES unity test_utils button)

View File

@@ -0,0 +1,234 @@
// 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 "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
#include "esp_log.h"
#include "unity.h"
#include "iot_button.h"
static const char *TAG = "BUTTON TEST";
#define BUTTON_IO_NUM 0
#define BUTTON_ACTIVE_LEVEL 0
#define BUTTON_NUM 16
static button_handle_t g_btns[BUTTON_NUM] = {0};
static int get_btn_index(button_handle_t btn)
{
for (size_t i = 0; i < BUTTON_NUM; i++) {
if (btn == g_btns[i]) {
return i;
}
}
return -1;
}
static void button_press_down_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_DOWN, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_DOWN", get_btn_index((button_handle_t)arg));
}
static void button_press_up_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_UP, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_UP", get_btn_index((button_handle_t)arg));
}
static void button_press_repeat_cb(void *arg)
{
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT[%d]", get_btn_index((button_handle_t)arg), iot_button_get_repeat((button_handle_t)arg));
}
static void button_single_click_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_SINGLE_CLICK, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_SINGLE_CLICK", get_btn_index((button_handle_t)arg));
}
static void button_double_click_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_DOUBLE_CLICK, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_DOUBLE_CLICK", get_btn_index((button_handle_t)arg));
}
static void button_long_press_start_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_START, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_START", get_btn_index((button_handle_t)arg));
}
static void button_long_press_hold_cb(void *arg)
{
TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_HOLD, iot_button_get_event(arg));
ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_HOLD", get_btn_index((button_handle_t)arg));
}
static void print_button_event(button_handle_t btn)
{
button_event_t evt = iot_button_get_event(btn);
switch (evt) {
case BUTTON_PRESS_DOWN:
ESP_LOGI(TAG, "BUTTON_PRESS_DOWN");
break;
case BUTTON_PRESS_UP:
ESP_LOGI(TAG, "BUTTON_PRESS_UP");
break;
case BUTTON_PRESS_REPEAT:
ESP_LOGI(TAG, "BUTTON_PRESS_REPEAT");
break;
case BUTTON_SINGLE_CLICK:
ESP_LOGI(TAG, "BUTTON_SINGLE_CLICK");
break;
case BUTTON_DOUBLE_CLICK:
ESP_LOGI(TAG, "BUTTON_DOUBLE_CLICK");
break;
case BUTTON_LONG_PRESS_START:
ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START");
break;
case BUTTON_LONG_PRESS_HOLD:
ESP_LOGI(TAG, "BUTTON_LONG_PRESS_HOLD");
break;
default:
break;
}
}
TEST_CASE("gpio button test", "[button][iot]")
{
button_config_t cfg = {
.type = BUTTON_TYPE_GPIO,
.gpio_button_config = {
.gpio_num = 0,
.active_level = 0,
},
};
g_btns[0] = iot_button_create(&cfg);
TEST_ASSERT_NOT_NULL(g_btns[0]);
iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb);
iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb);
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb);
iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb);
iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb);
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb);
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb);
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
iot_button_delete(g_btns[0]);
}
TEST_CASE("adc button test", "[button][iot]")
{
/** ESP32-LyraT-Mini board */
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
button_config_t cfg = {0};
cfg.type = BUTTON_TYPE_ADC;
for (size_t i = 0; i < 6; i++) {
cfg.adc_button_config.adc_channel = 3,
cfg.adc_button_config.button_index = i;
if (i == 0) {
cfg.adc_button_config.min = (0 + vol[i]) / 2;
} else {
cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
}
if (i == 5) {
cfg.adc_button_config.max = (vol[i] + 3000) / 2;
} else {
cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
}
g_btns[i] = iot_button_create(&cfg);
TEST_ASSERT_NOT_NULL(g_btns[i]);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb);
iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb);
iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb);
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb);
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb);
}
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
for (size_t i = 0; i < 6; i++) {
iot_button_delete(g_btns[i]);
}
}
TEST_CASE("adc gpio button test", "[button][iot]")
{
button_config_t cfg = {
.type = BUTTON_TYPE_GPIO,
.gpio_button_config = {
.gpio_num = 0,
.active_level = 0,
},
};
g_btns[8] = iot_button_create(&cfg);
TEST_ASSERT_NOT_NULL(g_btns[8]);
iot_button_register_cb(g_btns[8], BUTTON_PRESS_DOWN, button_press_down_cb);
iot_button_register_cb(g_btns[8], BUTTON_PRESS_UP, button_press_up_cb);
iot_button_register_cb(g_btns[8], BUTTON_PRESS_REPEAT, button_press_repeat_cb);
iot_button_register_cb(g_btns[8], BUTTON_SINGLE_CLICK, button_single_click_cb);
iot_button_register_cb(g_btns[8], BUTTON_DOUBLE_CLICK, button_double_click_cb);
iot_button_register_cb(g_btns[8], BUTTON_LONG_PRESS_START, button_long_press_start_cb);
iot_button_register_cb(g_btns[8], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb);
/** ESP32-LyraT-Mini board */
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
// button_config_t cfg = {0};
cfg.type = BUTTON_TYPE_ADC;
for (size_t i = 0; i < 6; i++) {
cfg.adc_button_config.adc_channel = 3,
cfg.adc_button_config.button_index = i;
if (i == 0) {
cfg.adc_button_config.min = (0 + vol[i]) / 2;
} else {
cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
}
if (i == 5) {
cfg.adc_button_config.max = (vol[i] + 3000) / 2;
} else {
cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
}
g_btns[i] = iot_button_create(&cfg);
TEST_ASSERT_NOT_NULL(g_btns[i]);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb);
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb);
iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb);
iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb);
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb);
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb);
}
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
for (size_t i = 0; i < 6; i++) {
iot_button_delete(g_btns[i]);
}
}

View File

@@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View File

@@ -0,0 +1,3 @@
set(COMPONENT_SRCS "cJSON_Utils.c" "cJSON.c")
set(COMPONENT_ADD_INCLUDEDIRS ". include")
register_component()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,285 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else /* !__WINDOWS__ */
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 14
#include <stddef.h>
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* Create a string where valuestring references a string so
* it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
* they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable adress area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,85 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON_Utils__h
#define cJSON_Utils__h
#ifdef __cplusplus
extern "C"
{
#endif
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
/* Utility for generating patch array entries. */
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
/* Returns 0 for success. */
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
//{
// cJSON *modme = cJSON_Duplicate(*object, 1);
// int error = cJSONUtils_ApplyPatches(modme, patches);
// if (!error)
// {
// cJSON_Delete(*object);
// *object = modme;
// }
// else
// {
// cJSON_Delete(modme);
// }
//
// return error;
//}
// Code not added to library since this strategy is a LOT slower.
*/
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
/* target will be modified by patch. return value is new ptr for target. */
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
/* generates a patch to move from -> to */
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
/* Given a root object and a target object, construct a pointer from one to the other. */
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
/* Sorts the members of the object into alphabetical order. */
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,3 @@
set(COMPONENT_SRCS "example.c")
set(COMPONENT_ADD_INCLUDEDIRS ". include")
register_component()

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,7 @@
#include "example.h"
#include <stdio.h>
void example()
{
printf("example\n");
}

View File

@@ -0,0 +1,9 @@
#ifndef _IOT_EXAMPLE2_H_
#define _IOT_EXAMPLE2_H_
void example();
#endif

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "sht3x.c"
INCLUDE_DIRS include
REQUIRES bus)

View File

@@ -0,0 +1,17 @@
# SHT3x
- This component will show you how to use I2C module read external i2c sensor data, here we use SHT3x-series temperature and humidity sensor(SHT30 is used this component).
- Pin assignment:
- GPIO21 is assigned as the data signal of i2c master port
- GPIO22 is assigned as the clock signal of i2c master port
- Connection:
* connect sda of sensor with GPIO21
* connect scl of sensor with GPIO22
- SHT3x measurement mode:
* single shot data acquisition mode: in this mode one issued measurement command triggers the acquisition of one data pair.
* periodic data acquisition mode: in this mode one issued measurement command yields a stream of data pairs. when use periodic data acquisition mode, you should send hex code 0xE000 firstly, and send 0x3093 to stop periodic mode.
# Notice
- SHT3x uses 8-bit CRC checksum, you can see `CheckCrc8() `for detail.
- The raw measurement data needs to be convert to physical scale. formulas are shown in `sht3x_get_humiture()`

View File

@@ -0,0 +1,8 @@
#
# "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,233 @@
// 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.
#ifndef _SHT3x_H_
#define _SHT3x_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include "driver/i2c.h"
#include "i2c_bus.h"
#include "esp_log.h"
#include "math.h"
typedef enum {
SOFT_RESET_CMD = 0x30A2, /*!< Command to soft reset*/
READOUT_FOR_PERIODIC_MODE = 0xE000, /*!< Command to read Periodic*/
READ_SERIAL_NUMBER = 0x3780, /*!< Command to read senser number*/
SHT3x_STOP_PERIODIC = 0x3093, /*!< Command to break or stop periodic mode*/
SHT3x_ART_CMD = 0x2B32, /*!< Command to accelerated response time*/
/* Single Shot Data Acquisition Mode*/
SHT3x_SINGLE_HIGH_ENABLED = 0x2C06, /*!< Command to set measure mode as Single Shot Data Acquisition mode in high repeatability and Clock Stretching enabled*/
SHT3x_SINGLE_MEDIUM_ENABLED = 0x2C0D, /*!< Command to set measure mode as Single Shot Data Acquisition mode in medium repeatability and Clock Stretching enabled*/
SHT3x_SINGLE_LOW_ENABLED = 0x2C10, /*!< Command to set measure mode as Single Shot Data Acquisition mode in low repeatability and Clock Stretching enabled*/
SHT3x_SINGLE_HIGH_DISABLED = 0x2400, /*!< Command to set measure mode as Single Shot Data Acquisition mode in high repeatability and Clock Stretching disabled*/
SHT3x_SINGLE_MEDIUM_DISABLED = 0x240B, /*!< Command to set measure mode as Single Shot Data Acquisition mode in medium repeatability and Clock Stretching disabled*/
SHT3x_SINGLE_LOW_DISABLED = 0x2416, /*!< Command to set measure mode as Single Shot Data Acquisition mode in low repeatability and Clock Stretching disabled*/
/* Periodic Data Acquisition mode*/
SHT3x_PER_0_5_HIGH = 0x2032, /*!< Command to set measure mode as Periodic Data Acquisition mode in high repeatability and 0.5 mps*/
SHT3x_PER_0_5_MEDIUM = 0x2024, /*!< Command to set measure mode as Periodic Data Acquisition mode in medium repeatability and 0.5 mps*/
SHT3x_PER_0_5_LOW = 0x202F, /*!< Command to set measure mode as Periodic Data Acquisition mode in low repeatability and 0.5 mps*/
SHT3x_PER_1_HIGH = 0x2130, /*!< Command to set measure mode as Periodic Data Acquisition mode in high repeatability and 1 mps*/
SHT3x_PER_1_MEDIUM = 0x2126, /*!< Command to set measure mode as Periodic Data Acquisition mode in medium repeatability and 1 mps*/
SHT3x_PER_1_LOW = 0x212D, /*!< Command to set measure mode as Periodic Data Acquisition mode in low repeatability and 1 mps*/
SHT3x_PER_2_HIGH = 0x2236, /*!< Command to set measure mode as Periodic Data Acquisition mode in high repeatability and 2 mps*/
SHT3x_PER_2_MEDIUM = 0x2220, /*!< Command to set measure mode as Periodic Data Acquisition mode in medium repeatability and 2 mps*/
SHT3x_PER_2_LOW = 0x222B, /*!< Command to set measure mode as Periodic Data Acquisition mode in low repeatability and 2 mps*/
SHT3x_PER_4_HIGH = 0x2334, /*!< Command to set measure mode as Periodic Data Acquisition mode in high repeatability and 4 mps*/
SHT3x_PER_4_MEDIUM = 0x2322, /*!< Command to set measure mode as Periodic Data Acquisition mode in medium repeatability and 4 mps*/
SHT3x_PER_4_LOW = 0x2329, /*!< Command to set measure mode as Periodic Data Acquisition mode in low repeatability and 4 mps*/
SHT3x_PER_10_HIGH = 0x2737, /*!< Command to set measure mode as Periodic Data Acquisition mode in high repeatability and 10 mps*/
SHT3x_PER_10_MEDIUM = 0x2721, /*!< Command to set measure mode as Periodic Data Acquisition mode in medium repeatability and 10 mps*/
SHT3x_PER_10_LOW = 0x272A, /*!< Command to set measure mode as Periodic Data Acquisition mode in low repeatability and 10 mps*/
/* cmd for sht3x heater condition*/
SHT3x_HEATER_ENABLE = 0x306D, /*!< Command to enable the heater*/
SHT3x_HEATER_DISABLED = 0x3066, /*!< Command to disable the heater*/
} sht3x_cmd_measure_t;
typedef enum {
SHT3x_ADDR_PIN_SELECT_VSS = 0x44, /*!< set address PIN select VSS */
SHT3x_ADDR_PIN_SELECT_VDD = 0x45, /*!< set address PIN select VDD */
} sht3x_set_address_t;
typedef void *sht3x_handle_t;
/**
* @brief Create sht3x handle_t
*
* @param bus sensorice object handle of sht3x
* @param dev_addr sensorice address
*
* @return
* - sht3x handle_t
*/
sht3x_handle_t sht3x_create(i2c_bus_handle_t bus, uint8_t dev_addr);
/**
* @brief Delete sht3x handle_t
*
* @param sensor point to sensorice object handle of sht3x
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_delete(sht3x_handle_t *sensor);
/**
* @brief Get temperature and humidity
*
* @param sensor object handle of shd3x
* @param Tem_val temperature data buffer
* @param Hum_val humidity data buffer
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_get_humiture(sht3x_handle_t sensor, float *Tem_val, float *Hum_val);
/**
* @brief Get temperature and humidity just once
*
* @param sensor object handle of shd3x
* @param Tem_val temperature data
* @param Hum_val humidity data
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_get_single_shot(sht3x_handle_t sensor, float *Tem_val, float *Hum_val);
/**
* @brief Soft reset for sht3x
*
* @param sensor object handle of sht3x
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_soft_reset(sht3x_handle_t sensor);
/**
* @brief stop or break or stop periodic mode
*
* @param sensor object handle of sht3x
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_stop_periodic(sht3x_handle_t sensor);
/**
* @brief accelerated response time
*
* @param sensor object handle of sht3x
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_art(sht3x_handle_t sensor);
/**
* @brief set measure mode of sht3x
*
* @param sensor object handle of shd3x
* @param sht3x_cmd_measure_t the instruction to set measurement mode
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t sht3x_set_measure_mode(sht3x_handle_t sensor, sht3x_cmd_measure_t sht3x_measure_mode);
/**
* @brief change the condition of sht3x heater
*
* @param sensor object handle of shd3x
* @param sht3x_cmd_measure_t the instruction to turn on/off heater of sht3x
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
* @note
* the default condition of heater is disabled
*/
esp_err_t sht3x_heater(sht3x_handle_t sensor, sht3x_cmd_measure_t sht3x_heater_condition);
/***implements of humiture hal interface****/
#ifdef CONFIG_SENSOR_HUMITURE_INCLUDED_SHT3X
/**
* @brief initialize sht3x with default configurations
*
* @param i2c_bus i2c bus handle the sensor will attached to
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t humiture_sht3x_init(i2c_bus_handle_t handle);
/**
* @brief de-initialize sht3x
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t humiture_sht3x_deinit(void);
/**
* @brief test if sht3x is active
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t humiture_sht3x_test(void);
/**
* @brief acquire relative humidity result one time.
*
* @param h point to result data (unit:percentage)
* @return esp_err_t
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t humiture_sht3x_acquire_humidity(float *h);
/**
* @brief acquire temperature result one time.
*
* @param t point to result data (unit:dCelsius)
* @return esp_err_t
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t humiture_sht3x_acquire_temperature(float *t);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,318 @@
// 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 "driver/i2c.h"
#include "i2c_bus.h"
#include "esp_log.h"
#include "esp_system.h"
#include "sht3x.h"
typedef struct {
i2c_bus_device_handle_t i2c_dev;
uint8_t dev_addr;
} sht3x_sensor_t;
sht3x_handle_t sht3x_create(i2c_bus_handle_t bus, uint8_t dev_addr)
{
sht3x_sensor_t *sens = (sht3x_sensor_t *) calloc(1, sizeof(sht3x_sensor_t));
sens->i2c_dev = i2c_bus_device_create(bus, dev_addr, i2c_bus_get_current_clk_speed(bus));
if (sens->i2c_dev == NULL) {
free(sens);
return NULL;
}
sens->dev_addr = dev_addr;
return (sht3x_handle_t) sens;
}
esp_err_t sht3x_delete(sht3x_handle_t *sensor)
{
if (*sensor == NULL) {
return ESP_OK;
}
sht3x_sensor_t *sens = (sht3x_sensor_t *)(*sensor);
i2c_bus_device_delete(&sens->i2c_dev);
free(sens);
*sensor = NULL;
return ESP_OK;
}
static esp_err_t sht3x_write_cmd(sht3x_handle_t sensor, sht3x_cmd_measure_t sht3x_cmd)
{
sht3x_sensor_t *sens = (sht3x_sensor_t *) sensor;
uint8_t cmd_buffer[2];
cmd_buffer[0] = sht3x_cmd >> 8;
cmd_buffer[1] = sht3x_cmd;
esp_err_t ret = i2c_bus_write_bytes(sens->i2c_dev, NULL_I2C_MEM_ADDR, 2, cmd_buffer);
return ret;
}
static esp_err_t sht3x_get_data(sht3x_handle_t sensor, uint8_t data_len, uint8_t *data_arr)
{
sht3x_sensor_t *sens = (sht3x_sensor_t *) sensor;
esp_err_t ret = i2c_bus_read_bytes(sens->i2c_dev, NULL_I2C_MEM_ADDR, data_len, data_arr);
return ret;
}
static uint8_t CheckCrc8(uint8_t *const message, uint8_t initial_value)
{
uint8_t crc;
int i = 0, j = 0;
crc = initial_value;
for (j = 0; j < 2; j++) {
crc ^= message[j];
for (i = 0; i < 8; i++) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0x31; /*!< 0x31 is Polynomial for 8-bit CRC checksum*/
} else {
crc = (crc << 1);
}
}
}
return crc;
}
esp_err_t sht3x_measure_period(bool set, uint16_t *min_delay)
{
static uint16_t s_min_delay = 250;
//get value
if (!set) {
*min_delay = s_min_delay;
return ESP_OK;
}
//set value
uint8_t delay_mode = *min_delay >> 8;
switch (delay_mode) {
case 0x20:
s_min_delay = 2000;
break;
case 0x21:
s_min_delay = 1000;
break;
case 0x22:
s_min_delay = 500;
break;
case 0x24:
s_min_delay = 250;
break;
case 0x27:
s_min_delay = 100;
break;
default:
s_min_delay = 250;
break;
}
return ESP_OK;
}
esp_err_t sht3x_get_humiture(sht3x_handle_t sensor, float *Tem_val, float *Hum_val)
{
uint8_t buff[6];
uint16_t tem, hum;
float Temperature = 0;
float Humidity = 0;
sht3x_write_cmd(sensor, READOUT_FOR_PERIODIC_MODE); /*!< if you want to read data just onetime, Comment this code*/
sht3x_get_data(sensor, 6, buff);
/* check crc */
if (CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5]) {
return ESP_FAIL;
}
tem = (((uint16_t)buff[0] << 8) | buff[1]);
Temperature = (175.0 * (float)tem / 65535.0 - 45.0) ; /*!< T = -45 + 175 * tem / (2^16-1), this temperature conversion formula is for Celsius °C */
//Temperature= (315.0*(float)tem/65535.0-49.0) ; /*!< T = -45 + 175 * tem / (2^16-1), this temperature conversion formula is for Fahrenheit °F */
hum = (((uint16_t)buff[3] << 8) | buff[4]);
Humidity = (100.0 * (float)hum / 65535.0); /*!< RH = hum*100 / (2^16-1) */
if ((Temperature >= -20) && (Temperature <= 125) && (Humidity >= 0) && (Humidity <= 100)) {
*Tem_val = Temperature;
*Hum_val = Humidity;
return ESP_OK; /*!< here is mesurement range */
} else {
return ESP_FAIL;
}
}
esp_err_t sht3x_get_single_shot(sht3x_handle_t sensor, float *Tem_val, float *Hum_val)
{
uint8_t buff[6];
uint16_t tem, hum;
static float Temperature = 0;
static float Humidity = 0;
static int64_t last_shot_time = 0;
int64_t current_time = esp_timer_get_time();
uint16_t min_delay = 0;
int64_t min_delay_us = 0;
sht3x_measure_period(false, &min_delay);
min_delay_us = min_delay * 1000;
if ((current_time - last_shot_time) < min_delay_us) {
*Tem_val = Temperature;
*Hum_val = Humidity;
return ESP_OK;
}
esp_err_t ret = sht3x_get_data(sensor, 6, buff);
/* check crc */
if (ret != ESP_OK || CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5]) {
return ESP_FAIL;
}
last_shot_time = current_time;
tem = (((uint16_t)buff[0] << 8) | buff[1]);
Temperature = (175.0 * (float)tem / 65535.0 - 45.0) ; /*!< T = -45 + 175 * tem / (2^16-1), this temperature conversion formula is for Celsius °C */
//Temperature= (315.0*(float)tem/65535.0-49.0) ; /*!< T = -45 + 175 * tem / (2^16-1), this temperature conversion formula is for Fahrenheit °F */
hum = (((uint16_t)buff[3] << 8) | buff[4]);
Humidity = (100.0 * (float)hum / 65535.0); /*!< RH = hum*100 / (2^16-1) */
if ((Temperature >= -20) && (Temperature <= 125) && (Humidity >= 0) && (Humidity <= 100)) {
*Tem_val = Temperature;
*Hum_val = Humidity;
return ESP_OK;
} else {
return ESP_FAIL;
}
}
esp_err_t sht3x_soft_reset(sht3x_handle_t sensor)
{
esp_err_t ret = sht3x_write_cmd(sensor, SOFT_RESET_CMD);
return ret;
}
esp_err_t sht3x_stop_periodic(sht3x_handle_t sensor)
{
esp_err_t ret = sht3x_write_cmd(sensor, SHT3x_STOP_PERIODIC);
return ret;
}
esp_err_t sht3x_art(sht3x_handle_t sensor)
{
esp_err_t ret = sht3x_write_cmd(sensor, SHT3x_ART_CMD);
return ret;
}
esp_err_t sht3x_set_measure_mode(sht3x_handle_t sensor, sht3x_cmd_measure_t sht3x_measure_mode)
{
esp_err_t ret = sht3x_write_cmd(sensor, sht3x_measure_mode);
sht3x_measure_period(true, (uint16_t *)&sht3x_measure_mode);
return ret;
}
esp_err_t sht3x_heater(sht3x_handle_t sensor, sht3x_cmd_measure_t sht3x_heater_condition)
{
esp_err_t ret = sht3x_write_cmd(sensor, sht3x_heater_condition);
return ret;
}
#ifdef CONFIG_SENSOR_HUMITURE_INCLUDED_SHT3X
static sht3x_handle_t sht3x = NULL;
static bool is_init = false;
esp_err_t humiture_sht3x_init(i2c_bus_handle_t i2c_bus)
{
if (is_init || !i2c_bus) {
return ESP_FAIL;
}
sht3x = sht3x_create(i2c_bus, SHT3x_ADDR_PIN_SELECT_VSS);
if (!sht3x) {
return ESP_FAIL;
}
esp_err_t ret = sht3x_set_measure_mode(sht3x, SHT3x_PER_4_MEDIUM); /**medium accuracy/repeatability with 250ms period (1000ms/4)**/
if (ret != ESP_OK) {
return ESP_FAIL;
}
is_init = true;
return ESP_OK;
}
esp_err_t humiture_sht3x_deinit(void)
{
if (!is_init) {
return ESP_FAIL;
}
esp_err_t ret = sht3x_delete(&sht3x);
if (ret != ESP_OK) {
return ESP_FAIL;
}
is_init = false;
return ESP_OK;
}
esp_err_t humiture_sht3x_test(void)
{
if (!is_init) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t humiture_sht3x_acquire_humidity(float *h)
{
if (!is_init) {
return ESP_FAIL;
}
float temperature = 0;
float humidity = 0;
esp_err_t ret = sht3x_get_single_shot(sht3x, &temperature, &humidity);
if (ret == ESP_OK) {
*h = humidity;
return ESP_OK;
}
*h = 0;
return ESP_FAIL;
}
esp_err_t humiture_sht3x_acquire_temperature(float *t)
{
if (!is_init) {
return ESP_FAIL;
}
float temperature = 0;
float humidity = 0;
esp_err_t ret = sht3x_get_single_shot(sht3x, &temperature, &humidity);
if (ret == ESP_OK) {
*t = temperature;
return ESP_OK;
}
*t = 0;
return ESP_FAIL;
}
#endif

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "sht3x_test.c"
INCLUDE_DIRS .
REQUIRES unity sht3x bus)

View File

@@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View File

@@ -0,0 +1,73 @@
// 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 "unity.h"
#include "driver/i2c.h"
#include "i2c_bus.h"
#include "esp_system.h"
#include "sht3x.h"
#define I2C_MASTER_SCL_IO (gpio_num_t)22 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (gpio_num_t)21 /*!< gpio number for I2C master data */
#define I2C_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
static i2c_bus_handle_t i2c_bus = NULL;
static sht3x_handle_t sht3x = NULL;
/**
* @brief i2c master initialization
*/
static void sht3x_init_test()
{
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
sht3x = sht3x_create(i2c_bus, SHT3x_ADDR_PIN_SELECT_VSS);
sht3x_set_measure_mode(sht3x, SHT3x_PER_2_MEDIUM); /*!< here read data in periodic mode*/
}
static void sht3x_deinit_test()
{
sht3x_delete(&sht3x);
i2c_bus_delete(&i2c_bus);
}
void sht3x_get_data_test()
{
float Tem_val, Hum_val;
int cnt = 10;
while (cnt--) {
if (sht3x_get_humiture(sht3x, &Tem_val, &Hum_val) == 0) {
printf("temperature %.2f°C ", Tem_val);
printf("humidity:%.2f %%\n", Hum_val);
}
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
TEST_CASE("Sensor sht3x test", "[sht3x][iot][sensor]")
{
sht3x_init_test();
vTaskDelay(1000 / portTICK_RATE_MS);
sht3x_get_data_test();
sht3x_deinit_test();
}