This commit is contained in:
2025-10-09 23:59:55 -04:00
parent 97db4618a2
commit 5706599df9
1473 changed files with 0 additions and 126166 deletions

View File

@@ -1 +0,0 @@
fe8f85b3859544fabb960d7bcec24278cd3c88e3f1777e8a2075b0aca202fd9f

View File

@@ -1,51 +0,0 @@
# ChangeLog
## v1.4.2 - 2025-8-26
### Bug Fix:
- Remove the dependency of `I2C_BUS_BACKWARD_CONFIG` on the IDF version, and add CMake messages for I2C driver information.
## V1.4.1 - 2025-8-14
### Bug Fix:
- Soft i2c supports removing the restriction on ``NULL_I2C_MEM_ADDR``, allowing users to refer to all eligible register addresses.
- Modify the `ESP_IDF_VERSION` naming in Kconfig to avoid conflicts with other components.
## v1.4.0 - 2025-3-13
### Enhancements:
- Support removing the restriction on ``NULL_I2C_MEM_ADDR``, allowing users to refer to all eligible register addresses.
## v1.3.0 - 2025-2-13
### Enhancements:
- ``i2c_bus_v2`` supports initialization using the bus_handle provided by ``esp_driver_i2c``, and also supports returning the internal bus_handle of ``esp_driver_i2c``.
## v1.2.0 - 2025-1-14
### Enhancements:
- Support enabling software I2C to extend the number of I2C ports.
## v1.1.0 - 2024-11-22
### Enhancements:
- Support manual selection of ``driver/i2c`` or ``esp_driver_i2c`` in idf v5.3 and above.
## v1.0.0 - 2024-9-19
### Enhancements:
- Component version maintenance and documentation enhancement.
- Support `esp_driver_i2c` driver.
## v0.1.0 - 2024-5-27
First release version.
- Support I2C bus

View File

@@ -1 +0,0 @@
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-08-27T10:45:51.231944+00:00", "files": [{"path": "CHANGELOG.md", "size": 1261, "hash": "3b1da89b95b6772f863f63da4d4c4c82001f28b5c93ea572c67089f657761578"}, {"path": "CMakeLists.txt", "size": 753, "hash": "fd22669ac3cece12a92742949c4608fb2e1da1439a8739a4a90c194dbc984c7b"}, {"path": "Kconfig", "size": 1931, "hash": "d25036af205c7f65f7e59938d28cc9526fd40a676b22ce3ff5da0dffc65827c8"}, {"path": "README.md", "size": 2910, "hash": "0dad1b9de95d531ddbbdf5daacd8b8ad0db8a908c0e1f716ef623d803995054c"}, {"path": "i2c_bus.c", "size": 25008, "hash": "f4f27a81aa3bca438b75240be396812b50f014a77e73f2e564ff54735559dd82"}, {"path": "i2c_bus_soft.c", "size": 15138, "hash": "3781e4dbb8971cbe94942a7a492042d0d30eef0a0b1a987f04f0e765264dcd6b"}, {"path": "i2c_bus_v2.c", "size": 27979, "hash": "ada5ca779030fcb26d7dd44955e6b3d348eae4d24cf54a4152c8f7c7e7536227"}, {"path": "idf_component.yml", "size": 543, "hash": "6ea3402fd8b698b1f2d508917af9cd9447c55dbfacce41d56757096005399205"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "include/i2c_bus.h", "size": 15491, "hash": "c5dd23aa628ab072f6e2b7edfa7e00fcc4233d7d234564c97156415447cb4833"}, {"path": "private_include/i2c_bus_soft.h", "size": 5089, "hash": "32433f38ca82faee70bfba1da0e1ad2fedea7c8477ab602fd337962ac4aec8f6"}, {"path": "test_apps/CMakeLists.txt", "size": 352, "hash": "7216690bc36de976c448f44ed31931ef8457acda922537c5e895f73897726cbe"}, {"path": "test_apps/pytest_i2c_bus.py", "size": 759, "hash": "910d205af99012638fae7c71356f79aa23a60655cf72d1df43ac0b1348468285"}, {"path": "test_apps/sdkconfig.defaults", "size": 257, "hash": "b3f9660085595b907f7411e205468220c2dfb867de4de5d7fb3cbb406651dcab"}, {"path": "test_apps/main/CMakeLists.txt", "size": 144, "hash": "82bd519669b040da7894ea50dc37fa85d2055a009e511ececa92a4e93497b4a1"}, {"path": "test_apps/main/test_i2c_bus.c", "size": 21112, "hash": "a5aa7e26f41845ad597867c9ce5e83a90975049cbc8a83a39434288b3d8ba084"}]}

View File

@@ -1,21 +0,0 @@
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.3" OR CONFIG_I2C_BUS_BACKWARD_CONFIG)
set(SRC_FILE "i2c_bus.c")
set(REQ driver)
message(STATUS "Using driver/i2c (SRC_FILE=i2c_bus.c, REQ=driver)")
else()
set(SRC_FILE "i2c_bus_v2.c")
set(REQ esp_driver_i2c driver)
message(STATUS "Using esp_driver_i2c (SRC_FILE=i2c_bus_v2.c, REQ=esp_driver_i2c driver)")
endif()
if (CONFIG_I2C_BUS_SUPPORT_SOFTWARE)
list(APPEND SRC_FILE "i2c_bus_soft.c")
endif()
idf_component_register(SRCS ${SRC_FILE}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include"
REQUIRES ${REQ})
include(package_manager)
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})

View File

@@ -1,48 +0,0 @@
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
config I2C_BUS_BACKWARD_CONFIG
bool "Enable backward compatibility for the I2C driver (force use of the old i2c_driver above v5.3)"
default n
help
Enable this option for backward compatibility with the old I2C driver (only valid in IDF v5.3 and above).
config I2C_BUS_SUPPORT_SOFTWARE
bool "Enable software I2C support"
default n
help
Enable this option to use a software-implemented I2C driver. This can be useful for scenarios where
hardware I2C is unavailable or additional I2C buses are needed beyond the hardware support.
config I2C_BUS_SOFTWARE_MAX_PORT
int "Maximum number of software I2C ports"
default 2
range 1 5
depends on I2C_BUS_SUPPORT_SOFTWARE
help
Set the maximum number of software I2C ports that can be used. This option is only applicable when
software I2C support is enabled.
config I2C_BUS_REMOVE_NULL_MEM_ADDR
bool "Remove the limitation of NULL_MEM_ADDR, any register address will be sent"
default n
help
Enable this option to disable NULL_MEM_ADDR. This allows any register address to be sent.
endmenu
endmenu

View File

@@ -1,70 +0,0 @@
# Component: I2C BUS
[Online documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/basic/bus/i2c_bus.html)
The I2C bus component (Bus) is a set of application-layer code built on top of the ESP-IDF peripheral driver code, It is mainly used for bus communication between ESP chips and external devices. From the point of application development, this component has the following features:
1. Simplified peripheral initialization processes
2. Thread-safe device operations
3. Simple and flexible RW operations
4. Compatible with `driver/i2c` and `esp_driver_i2c`
5. Supports additional software I2C
This component abstracts the following concepts:
1. Bus: the resource and configuration option shared between devices during communication
2. Device: device specific resource and configuration option during communication
Each physical peripheral bus can mount one or more devices if the electrical condition allows, the I2C bus addressing devices based on their addresses, thus achieving software independence between different devices on the same bus.
## Add component to your project
Please use the component manager command `add-dependency` to add the `i2c_bus` to your project's dependency, during the `CMake` step the component will be downloaded automatically
```
idf.py add-dependency "espressif/i2c_bus=*"
```
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
## Example use
```c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "i2c_bus.h"
#define I2C_MASTER_SCL_IO (gpio_num_t)15 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (gpio_num_t)16 /*!< gpio number for I2C master data */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
#define DATA_LENGTH 64 /*!<Data buffer length for test buffer*/
void app_main(void)
{
uint8_t *data_wr = (uint8_t *)malloc(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_NUM_0, &conf);
i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
for (int i = 0; i < DATA_LENGTH; i++)
{
data_wr[i] = i;
}
i2c_bus_write_bytes(i2c0_device1, NULL_I2C_MEM_ADDR, DATA_LENGTH, data_wr);
free(data_wr);
i2c_bus_device_delete(&i2c0_device1);
i2c_bus_delete(&i2c0_bus);
}
```

View File

@@ -1,595 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "i2c_bus.h"
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
#include "i2c_bus_soft.h"
#endif
#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 achieve thread-safe */
int32_t ref_counter; /*!< reference count */
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
i2c_master_soft_bus_handle_t soft_bus_handle; /*!< I2C master soft bus handle */
#endif
} 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";
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
static i2c_bus_t s_i2c_bus[I2C_NUM_SW_MAX]; /*!< If software I2C is enabled, additional space is required to store the port. */
#else
static i2c_bus_t s_i2c_bus[I2C_NUM_MAX];
#endif
#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, label) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
goto label; \
}
#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 = %"PRIu32"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 = %"PRIu32"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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", NULL);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "I2C port error", NULL);
#endif
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=%"PRIi32"", 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;
ESP_LOGI(TAG, "I2C Bus Config Succeed, Version: %d.%d.%d", I2C_BUS_VER_MAJOR, I2C_BUS_VER_MINOR, I2C_BUS_VER_PATCH);
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=%"PRIi32", 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;
esp_err_t ret = ESP_FAIL;
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, 0);
for (uint8_t dev_address = 1; dev_address < 127; dev_address++) {
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_bus->i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_probe(i2c_bus->soft_bus_handle, dev_address);
} else
#endif
{
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);
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_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;
esp_err_t ret = ESP_FAIL;
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
ret = i2c_master_soft_bus_read_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
} else
#endif
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
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);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
}
#endif
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);
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];
esp_err_t ret = ESP_OK;
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
ret = i2c_master_soft_bus_read_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
} else
#endif
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
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);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
}
#endif
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);
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;
esp_err_t ret = ESP_OK;
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
ret = i2c_master_soft_bus_write_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
} else
#endif
{
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 !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
i2c_master_write_byte(cmd, mem_address, I2C_ACK_CHECK_EN);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
}
#endif
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
i2c_master_stop(cmd);
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];
esp_err_t ret = ESP_OK;
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
ret = i2c_master_soft_bus_write_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
} else
#endif
{
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 !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
i2c_master_write(cmd, memAddress8, 2, I2C_ACK_CHECK_EN);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
}
#endif
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
i2c_master_stop(cmd);
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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
#endif
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
if (s_i2c_bus[port].is_init) {
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
} else
#endif
{
i2c_driver_delete(port);
}
s_i2c_bus[port].is_init = false;
ESP_LOGI(TAG, "i2c%d bus deinited", port);
}
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
ret = i2c_new_master_soft_bus(conf, &s_i2c_bus[port].soft_bus_handle);
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver install failed", ret);
} else
#endif
{
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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
#endif
I2C_BUS_CHECK(s_i2c_bus[port].is_init == true, "i2c not inited", ESP_ERR_INVALID_STATE);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
esp_err_t ret = i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver delete failed", ret);
} else
#endif
{
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

@@ -1,319 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h"
#include "esp_check.h"
#include "i2c_bus_soft.h"
#include "driver/gpio.h"
static const char*TAG = "i2c_bus_soft";
static esp_err_t i2c_master_soft_bus_wait_ack(i2c_master_soft_bus_handle_t bus_handle)
{
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
esp_rom_delay_us(bus_handle->time_delay_us);
bool ack = !gpio_get_level(bus_handle->sda_io); /*!< SDA should be low for ACK */
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
esp_rom_delay_us(bus_handle->time_delay_us);
return ack ? ESP_OK : ESP_ERR_NOT_FOUND;
}
static esp_err_t i2c_master_soft_bus_send_ack(i2c_master_soft_bus_handle_t bus_handle, bool ack)
{
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, ack ? 0 : 1), TAG, "Failed to set SDA for ACK/NACK"); /*!< Set SDA line to ACK (low) or NACK (high) */
// Generate clock pulse for ACK/NACK
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high during ACK/NACK");
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low after ACK/NACK");
esp_rom_delay_us(bus_handle->time_delay_us);
return ESP_OK;
}
static esp_err_t i2c_master_soft_bus_start(i2c_master_soft_bus_handle_t bus_handle)
{
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
esp_rom_delay_us(bus_handle->time_delay_us);
return ESP_OK;
}
static esp_err_t i2c_master_soft_bus_stop(i2c_master_soft_bus_handle_t bus_handle)
{
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
return ESP_OK;
}
static esp_err_t i2c_master_soft_bus_write_byte(i2c_master_soft_bus_handle_t bus_handle, uint8_t byte)
{
for (int i = 0; i < 8; i++) {
if (byte & 0x80) {
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
} else {
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
}
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
esp_rom_delay_us(bus_handle->time_delay_us);
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
if (i == 7) {
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to release SDA"); /*!< Release SDA */
}
byte <<= 1;
}
return ESP_OK;
}
static esp_err_t i2c_master_soft_bus_read_byte(i2c_master_soft_bus_handle_t bus_handle, uint8_t *byte)
{
uint8_t value = 0;
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to release SDA"); /*!< First release SDA */
for (int i = 0; i < 8; i++) {
value <<= 1;
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
esp_rom_delay_us(bus_handle->time_delay_us);
if (gpio_get_level(bus_handle->sda_io)) {
value ++;
}
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
esp_rom_delay_us(bus_handle->time_delay_us);
}
*byte = value;
return ESP_OK;
}
esp_err_t i2c_master_soft_bus_write_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, const uint8_t *data)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
// Send device address with write bit (0)
uint8_t address_byte = (dev_addr << 1) | 0;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, mem_address), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
}
#endif
// Write data
for (size_t i = 0; i < data_len; i++) {
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, data[i]), TAG, "Failed to write data byte");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for data byte");
}
// Generate STOP condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
return ESP_OK;
}
esp_err_t i2c_master_soft_bus_write_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, const uint8_t *data)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
// Send device address with write bit (0)
uint8_t address_byte = (dev_addr << 1) | 0;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)((mem_address >> 8) & 0x00FF)), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)(mem_address & 0x00FF)), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
}
#endif
// Write data
for (size_t i = 0; i < data_len; i++) {
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, data[i]), TAG, "Failed to write data byte");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for data byte");
}
// Generate STOP condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
return ESP_OK;
}
esp_err_t i2c_master_soft_bus_read_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, uint8_t *data)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
// Send memory address
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
// Generate START condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
// Send device address with write bit (0) to write the memory address
uint8_t write_address_byte = (dev_addr << 1) | 0;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, write_address_byte), TAG, "Failed to write device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, mem_address), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
}
#endif
// Generate RESTART condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate repeated start signal");
// Send device address with read bit (1)
uint8_t read_address_byte = (dev_addr << 1) | 1;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, read_address_byte), TAG, "Failed to write device address with read bit");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
// Read data bytes
for (size_t i = 0; i < data_len; i++) {
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_read_byte(bus_handle, &data[i]), TAG, "Failed to read data byte");
// Send ACK for all but the last byte
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_send_ack(bus_handle, i != data_len - 1), TAG, "Failed to send ACK/NACK");
}
// Generate STOP condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
return ESP_OK;
}
esp_err_t i2c_master_soft_bus_read_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, uint8_t *data)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
// Send memory address
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
// Generate START condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
// Send device address with write bit (0) to write the memory address
uint8_t write_address_byte = (dev_addr << 1) | 0;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, write_address_byte), TAG, "Failed to write device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)((mem_address >> 8) & 0x00FF)), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)(mem_address & 0x00FF)), TAG, "Failed to write memory address");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
}
#endif
// Generate RESTART condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate repeated start signal");
// Send device address with read bit (1)
uint8_t read_address_byte = (dev_addr << 1) | 1;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, read_address_byte), TAG, "Failed to write device address with read bit");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address during read");
// Read data bytes
for (size_t i = 0; i < data_len; i++) {
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_read_byte(bus_handle, &data[i]), TAG, "Failed to read data byte");
// Send ACK for all but the last byte
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_send_ack(bus_handle, i != data_len - 1), TAG, "Failed to send ACK/NACK");
}
// Generate STOP condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
return ESP_OK;
}
esp_err_t i2c_master_soft_bus_probe(i2c_master_soft_bus_handle_t bus_handle, uint8_t address)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
// Send device address with write bit (0)
uint8_t address_byte = (address << 1) | 0;
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write address byte");
// Wait for ACK
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "Failed to wait for ACK");
// Generate STOP condition
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
return ESP_OK;
}
esp_err_t i2c_new_master_soft_bus(const i2c_config_t *conf, i2c_master_soft_bus_handle_t *ret_soft_bus_handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(conf->scl_io_num) && GPIO_IS_VALID_GPIO(conf->sda_io_num), ESP_ERR_INVALID_ARG, TAG, "Invalid SDA/SCL pin number");
ESP_RETURN_ON_FALSE(conf->master.clk_speed > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid scl frequency");
gpio_config_t scl_io_conf = {
.mode = GPIO_MODE_OUTPUT_OD,
.pull_up_en = conf->scl_pullup_en,
.intr_type = GPIO_INTR_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pin_bit_mask = (1ULL << conf->scl_io_num),
};
ESP_RETURN_ON_ERROR(gpio_config(&scl_io_conf), TAG, "Failed to configure scl gpio");
gpio_config_t sda_io_conf = {
.mode = GPIO_MODE_INPUT_OUTPUT_OD,
.pull_up_en = conf->sda_pullup_en,
.intr_type = GPIO_INTR_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pin_bit_mask = (1ULL << conf->sda_io_num),
};
ESP_RETURN_ON_ERROR(gpio_config(&sda_io_conf), TAG, "Failed to configure sda gpio");
i2c_master_soft_bus_handle_t soft_bus_handle = calloc(1, sizeof(struct i2c_master_soft_bus_t));
if (soft_bus_handle == NULL) {
ESP_LOGE(TAG, "Failed to allocate soft bus handle");
return ESP_ERR_NO_MEM;
}
soft_bus_handle->scl_io = conf->scl_io_num;
soft_bus_handle->sda_io = conf->sda_io_num;
soft_bus_handle->time_delay_us = (uint32_t)((1e6f / conf->master.clk_speed) / 2.0f + 0.5f);
*ret_soft_bus_handle = soft_bus_handle;
return ret;
}
esp_err_t i2c_master_soft_bus_change_frequency(i2c_master_soft_bus_handle_t bus_handle, uint32_t frequency)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
ESP_RETURN_ON_FALSE(frequency > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid scl frequency");
bus_handle->time_delay_us = (uint32_t)((1e6f / frequency) / 2.0f + 0.5f);
return ESP_OK;
}
esp_err_t i2c_del_master_soft_bus(i2c_master_soft_bus_handle_t bus_handle)
{
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "no memory for i2c master soft bus");
free(bus_handle);
return ESP_OK;
}

View File

@@ -1,608 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "i2c_bus.h"
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
#include "i2c_bus_soft.h"
#endif
#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_PERIOD_MS)
#define I2C_BUS_MUTEX_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_PERIOD_MS)
typedef struct {
i2c_master_bus_config_t bus_config; /*!< I2C master bus specific configurations */
i2c_master_bus_handle_t bus_handle; /*!< I2C master bus handle */
i2c_device_config_t device_config; /*!< I2C device configuration, in order to get the frequency information of I2C */
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
i2c_master_soft_bus_handle_t soft_bus_handle; /*!< I2C master soft bus handle */
#endif
bool is_init; /*!< if bus is initialized */
i2c_config_t conf_activate; /*!< I2C active configuration */
SemaphoreHandle_t mutex; /*!< mutex to achieve thread-safe */
int32_t ref_counter; /*!< reference count */
} i2c_bus_t;
typedef struct {
i2c_device_config_t device_config; /*!< I2C device configuration */
i2c_master_dev_handle_t dev_handle; /*!< I2C master bus device handle */
i2c_device_config_t conf; /*!< I2C active configuration */
i2c_bus_t *i2c_bus; /*!< I2C bus */
} i2c_bus_device_t;
static const char *TAG = "i2c_bus";
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
static i2c_bus_t s_i2c_bus[I2C_NUM_SW_MAX]; /*!< If software I2C is enabled, additional space is required to store the port. */
#else
static i2c_bus_t s_i2c_bus[I2C_NUM_MAX];
#endif
#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, label) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
goto label; \
}
#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 = %"PRIu32"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 = %"PRIu32"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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", NULL);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "I2C port error", NULL);
#endif
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 (i2c_master_get_bus_handle(port, &s_i2c_bus[port].bus_handle) == ESP_OK) {
s_i2c_bus[port].is_init = true;
s_i2c_bus[port].conf_activate = *conf;
s_i2c_bus[port].bus_config.i2c_port = port;
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed;
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_LOGI(TAG, "I2C Bus V2 uses the externally initialized bus handle");
return (i2c_bus_handle_t)&s_i2c_bus[port];
}
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=%"PRIi32"", 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); /*!< Reconfigure the I2C parameters and initialise the bus. */
I2C_BUS_CHECK(ret == ESP_OK, "init error", NULL);
s_i2c_bus[port].conf_activate = *conf;
s_i2c_bus[port].bus_config.i2c_port = port;
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed; /*!< Stores the frequency configured in the bus, overrides the value if the device has a separate frequency */
ESP_LOGI(TAG, "I2C Bus V2 Config Succeed, Version: %d.%d.%d", I2C_BUS_VER_MAJOR, I2C_BUS_VER_MINOR, I2C_BUS_VER_PATCH);
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=%"PRIi32", won't be de-inited", i2c_bus->bus_config.i2c_port, i2c_bus->ref_counter);
return ESP_OK;
}
esp_err_t ret = i2c_driver_deinit(i2c_bus->bus_config.i2c_port);
I2C_BUS_CHECK(ret == ESP_OK, "deinit error", ret);
vSemaphoreDelete(i2c_bus->mutex);
*p_bus = NULL;
return ESP_OK;
}
i2c_master_bus_handle_t i2c_bus_get_internal_bus_handle(i2c_bus_handle_t bus_handle)
{
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", NULL);
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
I2C_BUS_INIT_CHECK(i2c_bus->is_init, NULL);
return i2c_bus->bus_handle;
}
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_activate.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->device_config.device_address = dev_addr;
i2c_device->device_config.dev_addr_length = I2C_ADDR_BIT_LEN_7;
i2c_device->device_config.scl_speed_hz = i2c_bus->device_config.scl_speed_hz; /*!< Transfer the frequency information in the bus to the device. */
i2c_device->device_config.flags.disable_ack_check = i2c_bus->device_config.flags.disable_ack_check; /*!< Transfer the ack check in the bus to the device. */
if (clk_speed != 0) {
i2c_device->device_config.scl_speed_hz = clk_speed;
}
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
// For software I2C, it is only necessary to save the device configuration.
if (i2c_bus->bus_config.i2c_port < I2C_NUM_MAX)
#endif
{
esp_err_t ret = i2c_master_bus_add_device(i2c_bus->bus_handle, &i2c_device->device_config, &i2c_device->dev_handle);
I2C_BUS_CHECK(ret == ESP_OK, "add device error", NULL);
}
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->bus_config.i2c_port < I2C_NUM_MAX)
#endif
{
esp_err_t ret = i2c_master_bus_rm_device(i2c_device->dev_handle);
I2C_BUS_CHECK(ret == ESP_OK, "remove device error", ret);
}
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
i2c_device->i2c_bus->ref_counter--; /*!< ref_counter is reduced only when the device is removed successfully. */
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_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;
esp_err_t ret = ESP_OK;
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, 0);
for (uint8_t dev_address = 1; dev_address < 127; dev_address++) {
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_probe(i2c_bus->soft_bus_handle, dev_address);
} else
#endif
{
ret = i2c_master_probe(i2c_bus->bus_handle, dev_address, 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_BUS_MUTEX_GIVE(i2c_bus->mutex, 0);
return device_count;
}
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->device_config.device_address;
}
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);
}
/**************************************** Public Functions (Low level)*********************************************/
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);
esp_err_t ret = ESP_FAIL;
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
// Need to distinguish between hardware I2C and software I2C via port
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
ret = i2c_master_soft_bus_read_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
} else
#endif
{
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
ret = i2c_master_transmit_receive(i2c_device->dev_handle, &mem_address, 1, data, data_len, I2C_BUS_TICKS_TO_WAIT);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
ret = i2c_master_receive(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
}
#endif
}
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);
esp_err_t ret = ESP_FAIL;
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);
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
// Need to distinguish between hardware I2C and software I2C via port
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
ret = i2c_master_soft_bus_read_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
} else
#endif
{
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
ret = i2c_master_transmit_receive(i2c_device->dev_handle, memAddress8, 2, data, data_len, I2C_BUS_TICKS_TO_WAIT);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
ret = i2c_master_receive(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
}
#endif
}
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);
esp_err_t ret = ESP_FAIL;
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
// Need to distinguish between hardware I2C and software I2C via port
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
ret = i2c_master_soft_bus_write_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
} else
#endif
{
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_ADDR) {
#endif
uint8_t *data_addr = malloc(data_len + 1);
if (data_addr == NULL) {
ESP_LOGE(TAG, "data_addr memory alloc fail");
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
return ESP_ERR_NO_MEM; /*!< If the memory request fails, unlock it immediately and return an error. */
}
data_addr[0] = mem_address;
for (int i = 0; i < data_len; i++) {
data_addr[i + 1] = data[i];
}
ret = i2c_master_transmit(i2c_device->dev_handle, data_addr, data_len + 1, I2C_BUS_TICKS_TO_WAIT);
free(data_addr);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
ret = i2c_master_transmit(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
}
#endif
}
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);
esp_err_t ret = ESP_FAIL;
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
ret = i2c_master_soft_bus_write_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
} else
#endif
{
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
#endif
uint8_t *data_addr = malloc(data_len + 2);
if (data_addr == NULL) {
ESP_LOGE(TAG, "data_addr memory alloc fail");
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
return ESP_ERR_NO_MEM; /*!< If the memory request fails, unlock it immediately and return an error. */
}
data_addr[0] = memAddress8[0];
data_addr[1] = memAddress8[1];
for (int i = 0; i < data_len; i++) {
data_addr[i + 2] = data[i];
}
ret = i2c_master_transmit(i2c_device->dev_handle, data_addr, data_len + 2, I2C_BUS_TICKS_TO_WAIT);
free(data_addr);
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
} else {
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
ret = i2c_master_transmit(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
}
#endif
}
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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
#endif
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
if (s_i2c_bus[port].is_init) {
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
} else
#endif
{
i2c_del_master_bus(s_i2c_bus[port].bus_handle);
}
s_i2c_bus[port].is_init = false;
ESP_LOGI(TAG, "i2c%d bus deinited", port);
}
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
s_i2c_bus[port].bus_config.i2c_port = port; /*!< Similarly, it is necessary to preserve the I2C_PORT for later distinction. */
esp_err_t ret = i2c_new_master_soft_bus(conf, &s_i2c_bus[port].soft_bus_handle);
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver install failed", ret);
s_i2c_bus[port].is_init = true;
ESP_LOGI(TAG, "i2c%d software bus inited", port);
} else
#endif
{
// Convert i2c_config_t information to i2c_master_bus_config_t and i2c_device_config_t
s_i2c_bus[port].bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
s_i2c_bus[port].bus_config.i2c_port = port;
s_i2c_bus[port].bus_config.scl_io_num = conf->scl_io_num;
s_i2c_bus[port].bus_config.sda_io_num = conf->sda_io_num;
s_i2c_bus[port].bus_config.glitch_ignore_cnt = 7; /*!< Set the burr cycle of the host bus */
s_i2c_bus[port].bus_config.flags.enable_internal_pullup = (conf->scl_pullup_en | conf->sda_pullup_en);
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed;
s_i2c_bus[port].device_config.flags.disable_ack_check = false;
esp_err_t ret = i2c_new_master_bus(&s_i2c_bus[port].bus_config, &s_i2c_bus[port].bus_handle);
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)
{
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
#else
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
#endif
I2C_BUS_CHECK(s_i2c_bus[port].is_init == true, "i2c not inited", ESP_ERR_INVALID_STATE);
// Need to distinguish between hardware I2C and software I2C via port
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
esp_err_t ret = i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver delete failed", ret);
} else
#endif
{
esp_err_t ret = i2c_del_master_bus(s_i2c_bus[port].bus_handle);
I2C_BUS_CHECK(ret == ESP_OK, "i2c driver delete failed", ret);
}
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 CONFIG_I2C_BUS_SUPPORT_SOFTWARE
if (port > I2C_NUM_MAX) {
if (s_i2c_bus[port].conf_activate.master.clk_speed == conf->master.clk_speed
&& s_i2c_bus[port].conf_activate.sda_io_num == conf->sda_io_num
&& s_i2c_bus[port].conf_activate.scl_io_num == conf->scl_io_num
&& s_i2c_bus[port].conf_activate.scl_pullup_en == conf->scl_pullup_en
&& s_i2c_bus[port].conf_activate.sda_pullup_en == conf->sda_pullup_en) {
return true;
}
} else
#endif
{
if (s_i2c_bus[port].bus_config.sda_io_num == conf->sda_io_num
&& s_i2c_bus[port].bus_config.scl_io_num == conf->scl_io_num
&& s_i2c_bus[port].bus_config.flags.enable_internal_pullup == (conf->scl_pullup_en | conf->sda_pullup_en)) {
return true;
}
}
return false;
}

View File

@@ -1,12 +0,0 @@
dependencies:
cmake_utilities: 0.*
idf: '>=4.0'
description: The I2C Bus Driver supports both hardware and software I2C.
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/basic/bus/i2c_bus.html
issues: https://github.com/espressif/esp-iot-solution/issues
repository: git://github.com/espressif/esp-iot-solution.git
repository_info:
commit_sha: 36792767ff23111c8fae8e57401d716e8da83e71
path: components/i2c_bus
url: https://github.com/espressif/esp-iot-solution/tree/master/components/i2c_bus
version: 1.4.2

View File

@@ -1,358 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _I2C_BUS_H_
#define _I2C_BUS_H_
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#if CONFIG_I2C_BUS_BACKWARD_CONFIG
#include "driver/i2c.h"
#else
#include "driver/i2c_master.h"
#endif
#else
#include "driver/i2c.h"
#endif
#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_MEM_16BIT_ADDR 0XFFFF /*!< set 16bit mem_address to NULL_I2C_MEM_16BIT_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
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
typedef enum {
I2C_NUM_SW_0 = I2C_NUM_MAX + 1,
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 2
I2C_NUM_SW_1,
#endif
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 3
I2C_NUM_SW_2,
#endif
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 4
I2C_NUM_SW_3,
#endif
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 5
I2C_NUM_SW_4,
#endif
I2C_NUM_SW_MAX,
} i2c_sw_port_t;
#endif
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
#define portTICK_RATE_MS portTICK_PERIOD_MS
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#if !CONFIG_I2C_BUS_BACKWARD_CONFIG
/**
* @brief I2C initialization parameters
*/
typedef struct {
i2c_mode_t mode; /*!< I2C mode */
int sda_io_num; /*!< GPIO number for I2C sda signal */
int scl_io_num; /*!< GPIO number for I2C scl signal */
bool sda_pullup_en; /*!< Internal GPIO pull mode for I2C sda signal*/
bool scl_pullup_en; /*!< Internal GPIO pull mode for I2C scl signal*/
struct {
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
} master; /*!< I2C master config */
uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
} i2c_config_t;
#endif
typedef void *i2c_cmd_handle_t; /*!< I2C command handle */
#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. Please note that enabling I2C_BUS_SUPPORT_SOFTWARE in menuconfig allows you to use ports in i2c_sw_port_t to enable software I2C.
* @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);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#if !CONFIG_I2C_BUS_BACKWARD_CONFIG
/**
* @brief Get internal idf bus_handle from i2c_bus_handle
*
* @param bus_handle I2C bus handle
* @return i2c_master_bus_handle_t Return the idf bus_handle if obtained successfully, return NULL if failed.
*/
i2c_master_bus_handle_t i2c_bus_get_internal_bus_handle(i2c_bus_handle_t bus_handle);
#endif
#endif
/**
* @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,
* higher 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

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,121 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "i2c_bus.h"
struct i2c_master_soft_bus_t {
gpio_num_t scl_io; /*!< SCL GPIO PIN */
gpio_num_t sda_io; /*!< SDA GPIO PIN */
uint32_t time_delay_us; /*!< Interval between SCL GPIO toggles in microseconds, determining the SCL frequency */
};
typedef struct i2c_master_soft_bus_t *i2c_master_soft_bus_handle_t;
/**
* @brief Allocate an I2C master soft bus
*
* @param conf I2C master soft bus configuration
* @param ret_soft_bus_handle I2C soft bus handle
* @return
* - ESP_OK: I2C master soft bus initialized successfully
* - ESP_ERR_INVALID_ARG: I2C soft bus initialization failed because of invalid argument
* - ESP_ERR_NO_MEM: Create I2C soft bus failed because of out of memory
*/
esp_err_t i2c_new_master_soft_bus(const i2c_config_t *conf, i2c_master_soft_bus_handle_t *ret_soft_bus_handle);
/**
* @brief Delete the I2C master soft bus
*
* @param bus_handle I2C soft bus handle
* @return
* - ESP_OK: Delete I2C soft bus success
* - Otherwise: Some module delete failed
*/
esp_err_t i2c_del_master_soft_bus(i2c_master_soft_bus_handle_t bus_handle);
/**
* @brief Change I2C soft bus frequency
*
* @param bus_handle I2C soft bus handle
* @param frequency I2C bus frequency
* @return
* - ESP_OK: Change I2C soft bus frequency success
* - ESP_ERR_INVALID_ARG: I2C soft bus change frequency failed because of invalid argument
*/
esp_err_t i2c_master_soft_bus_change_frequency(i2c_master_soft_bus_handle_t bus_handle, uint32_t frequency);
/**
* @brief Probe I2C address, if address is correct and ACK is received, this function will return ESP_OK
*
* @param bus_handle I2C soft bus handle
* @param address I2C device address that you want to probe
* @return
* - ESP_OK: I2C device probe successfully
* - ESP_ERR_INVALID_ARG: I2C probe failed because of invalid argument
* - ESP_ERR_NOT_FOUND: I2C probe failed, doesn't find the device with specific address you gave
*/
esp_err_t i2c_master_soft_bus_probe(i2c_master_soft_bus_handle_t bus_handle, uint8_t address);
/**
* @brief Write multiple byte to i2c device with 8-bit internal register/memory address
*
* @param bus_handle I2C soft bus handle
* @param dev_addr I2C device address
* @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_OK: I2C master write success
* - ESP_ERR_INVALID_ARG: I2C master write failed because of invalid argument
* - Otherwise: I2C master write failed
*/
esp_err_t i2c_master_soft_bus_write_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, const uint8_t *data);
/**
* @brief Write multiple byte to i2c device with 16-bit internal register/memory address
*
* @param bus_handle I2C soft bus handle
* @param dev_addr I2C device address
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_16BIT_ADDR if no internal address
* @param data_len Number of bytes to write
* @param data Pointer to the bytes to write
* @return
* - ESP_OK: I2C master write success
* - ESP_ERR_INVALID_ARG: I2C master write failed because of invalid argument
* - Otherwise: I2C master write failed
*/
esp_err_t i2c_master_soft_bus_write_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, const uint8_t *data);
/**
* @brief Read multiple byte to i2c device with 8-bit internal register/memory address
*
* @param bus_handle I2C soft bus handle
* @param dev_addr I2C device address
* @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 read
* @param data Pointer to the bytes to read
* @return
* - ESP_OK: I2C master read success
* - ESP_ERR_INVALID_ARG: I2C master read failed because of invalid argument
* - Otherwise: I2C master read failed
*/
esp_err_t i2c_master_soft_bus_read_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, uint8_t *data);
/**
* @brief Read multiple byte to i2c device with 16-bit internal register/memory address
*
* @param bus_handle I2C soft bus handle
* @param dev_addr I2C device address
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_16BIT_ADDR if no internal address
* @param data_len Number of bytes to read
* @param data Pointer to the bytes to read
* @return
* - ESP_OK: I2C master read success
* - ESP_ERR_INVALID_ARG: I2C master read failed because of invalid argument
* - Otherwise: I2C master read failed
*/
esp_err_t i2c_master_soft_bus_read_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, uint8_t *data);

View File

@@ -1,9 +0,0 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
"../../i2c_bus")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2c_bus_test)

View File

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

View File

@@ -1,595 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "unity_config.h"
#include "i2c_bus.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#include "driver/i2c_slave.h"
#include "test_utils.h"
#endif
#define TEST_MEMORY_LEAK_THRESHOLD (-460)
static size_t before_free_8bit;
static size_t before_free_32bit;
#define I2C_MASTER_SCL_IO (gpio_num_t)4 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (gpio_num_t)5 /*!< 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 (gpio_num_t)4 /*!<gpio number for i2c slave clock */
#define I2C_SLAVE_SDA_IO (gpio_num_t)5 /*!<gpio number for i2c slave data */
#define I2C_MASTER_NUM I2C_NUM_0 /*!<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 I2C_SCAN_ADDR_NUM (100) /*!< Number of slave addresses scanned by I2C */
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
static QueueHandle_t s_receive_queue;
static IRAM_ATTR bool test_i2c_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t high_task_wakeup = pdFALSE;
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
return high_task_wakeup == pdTRUE;
}
#endif
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 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);
}
// 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);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
unity_wait_for_signal("i2c slave init finish");
unity_send_signal("master write");
#endif
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);
free(data_wr);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
unity_wait_for_signal("ready to delete");
#endif
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);
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
int len = 0;
int size_rd = 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));
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);
TEST_ESP_OK(i2c_driver_delete(I2C_SLAVE_NUM));
#else
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_SLAVE_NUM,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = ESP_SLAVE_ADDR,
};
i2c_slave_dev_handle_t slave_handle;
TEST_ESP_OK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
s_receive_queue = xQueueCreate(1, sizeof(i2c_slave_rx_done_event_data_t));
i2c_slave_event_callbacks_t cbs = {
.on_recv_done = test_i2c_rx_done_callback,
};
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue));
i2c_slave_rx_done_event_data_t rx_data;
TEST_ESP_OK(i2c_slave_receive(slave_handle, data_rd, DATA_LENGTH / 2));
unity_send_signal("i2c slave init finish");
unity_wait_for_signal("master write");
xQueueReceive(s_receive_queue, &rx_data, pdMS_TO_TICKS(1000));
disp_buf(data_rd, DATA_LENGTH / 2);
for (int i = 0; i < DATA_LENGTH / 2; i++) {
TEST_ASSERT(data_rd[i] == i);
}
vQueueDelete(s_receive_queue);
unity_send_signal("ready to delete");
free(data_rd);
TEST_ESP_OK(i2c_del_slave_device(slave_handle));
#endif
}
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);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
unity_send_signal("i2c master init finish");
unity_wait_for_signal("slave write");
#endif
i2c_bus_read_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_rd);
vTaskDelay(100 / portTICK_RATE_MS);
disp_buf(data_rd, RW_TEST_LENGTH);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
unity_send_signal("ready to delete");
#endif
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);
free(data_rd);
}
static void slave_write_buffer_test(void)
{
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
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));
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = i;
}
i2c_slave_write_buffer(I2C_SLAVE_NUM, data_wr, RW_TEST_LENGTH, 2000 / portTICK_RATE_MS);
#else
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_SLAVE_NUM,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = ESP_SLAVE_ADDR,
};
i2c_slave_dev_handle_t slave_handle;
TEST_ESP_OK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = i;
}
unity_wait_for_signal("i2c master init finish");
unity_send_signal("slave write");
i2c_slave_transmit(slave_handle, data_wr, RW_TEST_LENGTH, 100 / portTICK_PERIOD_MS);
#endif
disp_buf(data_wr, RW_TEST_LENGTH);
free(data_wr);
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
i2c_driver_delete(I2C_SLAVE_NUM);
#else
unity_wait_for_signal("ready to delete");
TEST_ESP_OK(i2c_del_slave_device(slave_handle));
#endif
}
TEST_CASE_MULTIPLE_DEVICES("I2C master read slave test", "[i2c_bus]", master_read_slave_test, slave_write_buffer_test);
TEST_CASE("I2C master write under different frequency test", "[i2c_bus]")
{
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = 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 i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c_bus != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
TEST_ASSERT(i2c_device1 != NULL);
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
vTaskDelay(300 / portTICK_RATE_MS);
conf.master.clk_speed = 40 * 100;
i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c_bus, 0x02, 40 * 100);
TEST_ASSERT(i2c_device2 != NULL);
i2c_bus_write_bytes(i2c_device2, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
i2c_bus_device_delete(&i2c_device2);
TEST_ASSERT(i2c_device2 == NULL);
free(data_wr);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
TEST_ASSERT(i2c_bus == NULL);
}
TEST_CASE("i2c bus init-deinit test", "[bus][i2c_bus]")
{
i2c_bus_init_deinit_test();
i2c_bus_device_add_test();
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
TEST_CASE("I2C bus uses external bus handle test", "[bus][i2c_bus]")
{
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = i;
}
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_MASTER_NUM,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t bus_handle;
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
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 i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, 400000);
TEST_ASSERT(i2c_device1 != NULL);
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
i2c_bus_device_delete(&i2c_device1);
free(data_wr);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
TEST_ASSERT(i2c_bus == NULL);
}
#endif
TEST_CASE("I2C bus scan test", "[i2c_bus][scan]")
{
uint8_t addrs[I2C_SCAN_ADDR_NUM] = {0};
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 i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
i2c_bus_scan(i2c_bus, addrs, I2C_SCAN_ADDR_NUM);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
TEST_ASSERT(i2c_bus == NULL);
}
TEST_CASE("I2C bus register address restriction test", "[i2c_bus][NULL_I2C_MEM_ADDR]")
{
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = 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 i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
TEST_ASSERT(i2c_bus != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
TEST_ASSERT(i2c_device1 != NULL);
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
free(data_wr);
TEST_ASSERT(i2c_bus == NULL);
}
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
TEST_CASE("I2C soft bus init-deinit test", "[soft][bus][i2c_bus]")
{
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_SW_1, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
/** configs not change**/
i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_1, &conf);
TEST_ASSERT(i2c0_bus_1 != NULL);
/** configs change**/
conf.master.clk_speed *= 2;
i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_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);
}
TEST_CASE("I2C soft bus device add test", "[soft][bus][device][i2c_bus]")
{
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_SW_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);
}
TEST_CASE("I2C soft bus scan test", "[soft][i2c_bus][scan]")
{
uint8_t addrs[I2C_SCAN_ADDR_NUM] = {0};
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 i2c_bus = i2c_bus_create(I2C_NUM_SW_0, &conf);
i2c_bus_scan(i2c_bus, addrs, I2C_SCAN_ADDR_NUM);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
TEST_ASSERT(i2c_bus == NULL);
}
TEST_CASE("I2C soft bus write under different frequency test", "[soft][i2c_bus]")
{
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
for (int i = 0; i < RW_TEST_LENGTH; i++) {
data_wr[i] = 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 i2c_bus = i2c_bus_create(I2C_NUM_SW_0, &conf);
TEST_ASSERT(i2c_bus != NULL);
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
TEST_ASSERT(i2c_device1 != NULL);
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
vTaskDelay(300 / portTICK_RATE_MS);
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c_bus, 0x02, 40 * 100);
TEST_ASSERT(i2c_device2 != NULL);
i2c_bus_write_bytes(i2c_device2, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
i2c_bus_device_delete(&i2c_device1);
TEST_ASSERT(i2c_device1 == NULL);
i2c_bus_device_delete(&i2c_device2);
TEST_ASSERT(i2c_device2 == NULL);
free(data_wr);
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
TEST_ASSERT(i2c_bus == NULL);
}
#endif
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
printf("I2C BUS TEST \n");
#else
printf("I2C BUS V2 TEST \n");
#endif
unity_run_menu();
}

View File

@@ -1,30 +0,0 @@
# SPDX-FileCopyrightText: 2024Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
'''
Steps to run these cases:
- Build
- . ${IDF_PATH}/export.sh
- pip install idf_build_apps
- python tools/build_apps.py components/sensors/i2c_bus/test_apps -t esp32
- Test
- pip install -r tools/requirements/requirement.pytest.txt
- pytest components/sensors/i2c_bus/test_apps --target esp32
'''
import pytest
from pytest_embedded import Dut
@pytest.mark.target('esp32')
@pytest.mark.target('esp32c3')
@pytest.mark.target('esp32c6')
@pytest.mark.target('esp32s3')
@pytest.mark.env('generic')
@pytest.mark.parametrize(
'config',
[
'defaults',
],
)
def test_i2c_bus(dut: Dut)-> None:
dut.run_all_single_board_cases()

View File

@@ -1,10 +0,0 @@
# For IDF 5.0
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
# For IDF4.4
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP_TASK_WDT=n