/* * ESPRESSIF MIT License * * Copyright (c) 2017 * * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in * which case, it is 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. * */ #include "i2c_bus.h" #include #include #include "audio_mem.h" #include "audio_mutex.h" #include "driver/i2c.h" #include "esp_log.h" #define ESP_INTR_FLG_DEFAULT (0) #define ESP_I2C_MASTER_BUF_LEN (0) #define I2C_ACK_CHECK_EN 1 #define I2C_BUS_CHECK(a, str, ret) \ if (!(a)) { \ ESP_LOGE(TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ } typedef struct { i2c_config_t i2c_conf; /*!i2c_conf, sizeof(i2c_config_t)) == 0) { i2c_bus[port]->ref_count++; ESP_LOGW(TAG, "I2C bus has been already created, [port:%d]", port); return i2c_bus[port]; } else { ESP_LOGE(TAG, "Have not enough slot(%d) to create I2C bus", port); return NULL; } } i2c_bus[port] = (i2c_bus_t *)audio_calloc(1, sizeof(i2c_bus_t)); i2c_bus[port]->i2c_conf = *conf; i2c_bus[port]->i2c_port = port; i2c_bus[port]->ref_count++; esp_err_t ret = i2c_param_config(i2c_bus[port]->i2c_port, &i2c_bus[port]->i2c_conf); if (ret != ESP_OK) { goto error; } ret = i2c_driver_install(i2c_bus[port]->i2c_port, i2c_bus[port]->i2c_conf.mode, ESP_I2C_MASTER_BUF_LEN, ESP_I2C_MASTER_BUF_LEN, ESP_INTR_FLG_DEFAULT); if (ret != ESP_OK) { goto error; } i2c_bus[port]->bus_lock = mutex_create(); if (i2c_bus[port]->bus_lock == NULL) { goto error; } return (i2c_bus_handle_t)i2c_bus[port]; error: if (i2c_bus[port]) { if (i2c_bus[port]->bus_lock == NULL) { mutex_destroy(i2c_bus[port]->bus_lock); } audio_free(i2c_bus[port]); } return NULL; } esp_err_t i2c_bus_write_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg, int regLen, uint8_t *data, int datalen) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); i2c_bus_t *p_bus = (i2c_bus_t *)bus; I2C_BUS_CHECK(p_bus->i2c_port < I2C_NUM_MAX, "I2C port error", ESP_FAIL); I2C_BUS_CHECK(data != NULL, "Not initialized input data pointer", ESP_FAIL); esp_err_t ret = ESP_OK; mutex_lock(p_bus->bus_lock); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); ret |= i2c_master_start(cmd); ret |= i2c_master_write_byte(cmd, addr, 1); ret |= i2c_master_write(cmd, reg, regLen, I2C_ACK_CHECK_EN); ret |= i2c_master_write(cmd, data, datalen, I2C_ACK_CHECK_EN); ret |= i2c_master_stop(cmd); ret |= i2c_master_cmd_begin(p_bus->i2c_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); mutex_unlock(p_bus->bus_lock); I2C_BUS_CHECK(ret == 0, "I2C Bus WriteReg Error", ESP_FAIL); return ret; } esp_err_t i2c_bus_write_data(i2c_bus_handle_t bus, int addr, uint8_t *data, int datalen) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); i2c_bus_t *p_bus = (i2c_bus_t *)bus; I2C_BUS_CHECK(p_bus->i2c_port < I2C_NUM_MAX, "I2C port error", ESP_FAIL); I2C_BUS_CHECK(data != NULL, "Not initialized input data pointer", ESP_FAIL); esp_err_t ret = ESP_OK; mutex_lock(p_bus->bus_lock); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); ret |= i2c_master_start(cmd); ret |= i2c_master_write_byte(cmd, addr, 1); if (datalen) { ret |= i2c_master_write(cmd, data, datalen, I2C_ACK_CHECK_EN); } ret |= i2c_master_stop(cmd); ret |= i2c_master_cmd_begin(p_bus->i2c_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); mutex_unlock(p_bus->bus_lock); I2C_BUS_CHECK(ret == 0, "I2C Bus WriteReg Error", ESP_FAIL); return ret; } esp_err_t i2c_bus_read_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg, int reglen, uint8_t *outdata, int datalen) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); i2c_bus_t *p_bus = (i2c_bus_t *)bus; I2C_BUS_CHECK(p_bus->i2c_port < I2C_NUM_MAX, "I2C port error", ESP_FAIL); I2C_BUS_CHECK(outdata != NULL, "Not initialized output data buffer pointer", ESP_FAIL); esp_err_t ret = ESP_OK; mutex_lock(p_bus->bus_lock); i2c_cmd_handle_t cmd; cmd = i2c_cmd_link_create(); ret |= i2c_master_start(cmd); ret |= i2c_master_write_byte(cmd, addr, I2C_ACK_CHECK_EN); ret |= i2c_master_write(cmd, reg, reglen, I2C_ACK_CHECK_EN); ret |= i2c_master_stop(cmd); ret |= i2c_master_cmd_begin(p_bus->i2c_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); cmd = i2c_cmd_link_create(); ret |= i2c_master_start(cmd); ret |= i2c_master_write_byte(cmd, addr | 0x01, I2C_ACK_CHECK_EN); for (int i = 0; i < datalen - 1; i++) { ret |= i2c_master_read_byte(cmd, &outdata[i], 0); } ret |= i2c_master_read_byte(cmd, &outdata[datalen - 1], 1); ret |= i2c_master_stop(cmd); ret |= i2c_master_cmd_begin(p_bus->i2c_port, cmd, 1000 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); mutex_unlock(p_bus->bus_lock); I2C_BUS_CHECK(ret == 0, "I2C Bus ReadReg Error", ESP_FAIL); return ret; } esp_err_t i2c_bus_delete(i2c_bus_handle_t bus) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); i2c_bus_t *p_bus = (i2c_bus_t *)bus; if (--p_bus->ref_count == 0) { i2c_driver_delete(p_bus->i2c_port); i2c_bus[p_bus->i2c_port] = NULL; mutex_destroy(p_bus->bus_lock); audio_free(p_bus); } return ESP_OK; } esp_err_t i2c_bus_cmd_begin(i2c_bus_handle_t bus, i2c_cmd_handle_t cmd, portBASE_TYPE ticks_to_wait) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); I2C_BUS_CHECK(cmd != NULL, "I2C cmd error", ESP_FAIL); i2c_bus_t *p_bus = (i2c_bus_t *)bus; esp_err_t ret = i2c_master_cmd_begin(p_bus->i2c_port, cmd, ticks_to_wait); return ret; } esp_err_t i2c_bus_probe_addr(i2c_bus_handle_t bus, uint8_t addr) { I2C_BUS_CHECK(bus != NULL, "Handle error", ESP_FAIL); /* Use 7 bit address here */ if (addr >= 0x80) { ESP_LOGE(TAG, "I2C addr out of range"); return ESP_ERR_INVALID_ARG; } i2c_bus_t *i2c_bus = (i2c_bus_t *)bus; mutex_lock(i2c_bus->bus_lock); i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, addr, I2C_ACK_CHECK_EN); i2c_master_stop(cmd); esp_err_t ret_val = i2c_master_cmd_begin(i2c_bus->i2c_port, cmd, pdMS_TO_TICKS(500)); i2c_cmd_link_delete(cmd); mutex_unlock(i2c_bus->bus_lock); /* Get probe result if ESP_OK equals to ret_val */ return ret_val; }