Sun, 16 Apr 2023 12:27:12 +0200
Preparations for BLE GATT. Added extra time after INA219 is powered on before measurement. Reduced LEDC frequency to 60 Hz, that makes the LED lights less nervous. Hardware mod on output 4, now needs external pulldown resistor.
/* * The MIT License (MIT) * * Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com> * * 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. */ /** * @file i2cdev.c * * ESP-IDF I2C master thread-safe functions for communication with I2C slave * * Copyright (c) 2018 Ruslan V. Uss <unclerus@gmail.com> * * MIT Licensed as described in the file LICENSE */ #include <string.h> #include <inttypes.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #include <esp_log.h> #include "i2cdev.h" static const char *TAG = "i2cdev"; typedef struct { SemaphoreHandle_t lock; i2c_config_t config; bool installed; } i2c_port_state_t; static i2c_port_state_t states[I2C_NUM_MAX]; #if CONFIG_I2CDEV_NOLOCK #define SEMAPHORE_TAKE(port) #else #define SEMAPHORE_TAKE(port) do { \ if (!xSemaphoreTake(states[port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT))) \ { \ ESP_LOGE(TAG, "Could not take port mutex %d", port); \ return ESP_ERR_TIMEOUT; \ } \ } while (0) #endif #if CONFIG_I2CDEV_NOLOCK #define SEMAPHORE_GIVE(port) #else #define SEMAPHORE_GIVE(port) do { \ if (!xSemaphoreGive(states[port].lock)) \ { \ ESP_LOGE(TAG, "Could not give port mutex %d", port); \ return ESP_FAIL; \ } \ } while (0) #endif esp_err_t i2cdev_init() { memset(states, 0, sizeof(states)); #if !CONFIG_I2CDEV_NOLOCK for (int i = 0; i < I2C_NUM_MAX; i++) { states[i].lock = xSemaphoreCreateMutex(); if (!states[i].lock) { ESP_LOGE(TAG, "Could not create port mutex %d", i); return ESP_FAIL; } } #endif return ESP_OK; } esp_err_t i2cdev_done() { for (int i = 0; i < I2C_NUM_MAX; i++) { if (!states[i].lock) continue; if (states[i].installed) { SEMAPHORE_TAKE(i); i2c_driver_delete(i); states[i].installed = false; SEMAPHORE_GIVE(i); } #if !CONFIG_I2CDEV_NOLOCK vSemaphoreDelete(states[i].lock); #endif states[i].lock = NULL; } return ESP_OK; } esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev) { #if !CONFIG_I2CDEV_NOLOCK if (!dev) return ESP_ERR_INVALID_ARG; ESP_LOGV(TAG, "[0x%02x at %d] creating mutex", dev->addr, dev->port); dev->mutex = xSemaphoreCreateMutex(); if (!dev->mutex) { ESP_LOGE(TAG, "[0x%02x at %d] Could not create device mutex", dev->addr, dev->port); return ESP_FAIL; } #endif return ESP_OK; } esp_err_t i2c_dev_delete_mutex(i2c_dev_t *dev) { #if !CONFIG_I2CDEV_NOLOCK if (!dev) return ESP_ERR_INVALID_ARG; ESP_LOGV(TAG, "[0x%02x at %d] deleting mutex", dev->addr, dev->port); vSemaphoreDelete(dev->mutex); #endif return ESP_OK; } esp_err_t i2c_dev_take_mutex(i2c_dev_t *dev) { #if !CONFIG_I2CDEV_NOLOCK if (!dev) return ESP_ERR_INVALID_ARG; ESP_LOGV(TAG, "[0x%02x at %d] taking mutex", dev->addr, dev->port); if (!xSemaphoreTake(dev->mutex, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT))) { ESP_LOGE(TAG, "[0x%02x at %d] Could not take device mutex", dev->addr, dev->port); return ESP_ERR_TIMEOUT; } #endif return ESP_OK; } esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev) { #if !CONFIG_I2CDEV_NOLOCK if (!dev) return ESP_ERR_INVALID_ARG; ESP_LOGV(TAG, "[0x%02x at %d] giving mutex", dev->addr, dev->port); if (!xSemaphoreGive(dev->mutex)) { ESP_LOGE(TAG, "[0x%02x at %d] Could not give device mutex", dev->addr, dev->port); return ESP_FAIL; } #endif return ESP_OK; } inline static bool cfg_equal(const i2c_config_t *a, const i2c_config_t *b) { return a->scl_io_num == b->scl_io_num && a->sda_io_num == b->sda_io_num #if HELPER_TARGET_IS_ESP32 && a->master.clk_speed == b->master.clk_speed #elif HELPER_TARGET_IS_ESP8266 && a->clk_stretch_tick == b->clk_stretch_tick #endif && a->scl_pullup_en == b->scl_pullup_en && a->sda_pullup_en == b->sda_pullup_en; } static esp_err_t i2c_setup_port(const i2c_dev_t *dev) { if (dev->port >= I2C_NUM_MAX) return ESP_ERR_INVALID_ARG; esp_err_t res; if (!cfg_equal(&dev->cfg, &states[dev->port].config) || !states[dev->port].installed) { ESP_LOGD(TAG, "Reconfiguring I2C driver on port %d", dev->port); i2c_config_t temp; memcpy(&temp, &dev->cfg, sizeof(i2c_config_t)); temp.mode = I2C_MODE_MASTER; // Driver reinstallation if (states[dev->port].installed) { i2c_driver_delete(dev->port); states[dev->port].installed = false; } #if HELPER_TARGET_IS_ESP32 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) // See https://github.com/espressif/esp-idf/issues/10163 if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) return res; if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) return res; #else if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) return res; if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) return res; #endif #endif #if HELPER_TARGET_IS_ESP8266 // Clock Stretch time, depending on CPU frequency temp.clk_stretch_tick = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; if ((res = i2c_driver_install(dev->port, temp.mode)) != ESP_OK) return res; if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) return res; #endif states[dev->port].installed = true; memcpy(&states[dev->port].config, &temp, sizeof(i2c_config_t)); ESP_LOGD(TAG, "I2C driver successfully reconfigured on port %d", dev->port); } #if HELPER_TARGET_IS_ESP32 int t; if ((res = i2c_get_timeout(dev->port, &t)) != ESP_OK) return res; // Timeout cannot be 0 uint32_t ticks = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; if ((ticks != t) && (res = i2c_set_timeout(dev->port, ticks)) != ESP_OK) return res; ESP_LOGD(TAG, "Timeout: ticks = %" PRIu32 " (%" PRIu32 " usec) on port %d", dev->timeout_ticks, dev->timeout_ticks / 80, dev->port); #endif return ESP_OK; } esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type) { if (!dev) return ESP_ERR_INVALID_ARG; SEMAPHORE_TAKE(dev->port); esp_err_t res = i2c_setup_port(dev); if (res == ESP_OK) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, dev->addr << 1 | (operation_type == I2C_DEV_READ ? 1 : 0), true); i2c_master_stop(cmd); res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); i2c_cmd_link_delete(cmd); } SEMAPHORE_GIVE(dev->port); return res; } esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size) { if (!dev || !in_data || !in_size) return ESP_ERR_INVALID_ARG; SEMAPHORE_TAKE(dev->port); esp_err_t res = i2c_setup_port(dev); if (res == ESP_OK) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); if (out_data && out_size) { i2c_master_start(cmd); i2c_master_write_byte(cmd, dev->addr << 1, true); i2c_master_write(cmd, (void *)out_data, out_size, true); } i2c_master_start(cmd); i2c_master_write_byte(cmd, (dev->addr << 1) | 1, true); i2c_master_read(cmd, in_data, in_size, I2C_MASTER_LAST_NACK); i2c_master_stop(cmd); res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); if (res != ESP_OK) ESP_LOGE(TAG, "Could not read from device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); i2c_cmd_link_delete(cmd); } SEMAPHORE_GIVE(dev->port); return res; } esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size) { if (!dev || !out_data || !out_size) return ESP_ERR_INVALID_ARG; SEMAPHORE_TAKE(dev->port); esp_err_t res = i2c_setup_port(dev); if (res == ESP_OK) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, dev->addr << 1, true); if (out_reg && out_reg_size) i2c_master_write(cmd, (void *)out_reg, out_reg_size, true); i2c_master_write(cmd, (void *)out_data, out_size, true); i2c_master_stop(cmd); res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); if (res != ESP_OK) ESP_LOGE(TAG, "Could not write to device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); i2c_cmd_link_delete(cmd); } SEMAPHORE_GIVE(dev->port); return res; } esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *in_data, size_t in_size) { return i2c_dev_read(dev, ®, 1, in_data, in_size); } esp_err_t i2c_dev_write_reg(const i2c_dev_t *dev, uint8_t reg, const void *out_data, size_t out_size) { return i2c_dev_write(dev, ®, 1, out_data, out_size); }