mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 01:29:21 -04:00
feat(i2c): Add api for customize i2c transaction interface for un-standard i2c device
This commit is contained in:
parent
bd4f0b44f4
commit
c6161380d3
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -285,7 +285,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation
|
|||||||
i2c_master->contains_read = true;
|
i2c_master->contains_read = true;
|
||||||
#if !SOC_I2C_STOP_INDEPENDENT
|
#if !SOC_I2C_STOP_INDEPENDENT
|
||||||
if (remaining_bytes < I2C_FIFO_LEN(i2c_master->base->port_num) - 1) {
|
if (remaining_bytes < I2C_FIFO_LEN(i2c_master->base->port_num) - 1) {
|
||||||
if (i2c_operation->hw_cmd.ack_val == ACK_VAL) {
|
if (i2c_operation->hw_cmd.ack_val == I2C_ACK_VAL) {
|
||||||
if (remaining_bytes != 0) {
|
if (remaining_bytes != 0) {
|
||||||
i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx);
|
i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx);
|
||||||
i2c_master->read_len_static = i2c_master->rx_cnt;
|
i2c_master->read_len_static = i2c_master->rx_cnt;
|
||||||
@ -321,7 +321,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation
|
|||||||
portENTER_CRITICAL_SAFE(&handle->spinlock);
|
portENTER_CRITICAL_SAFE(&handle->spinlock);
|
||||||
// If the read command work with ack_val, but no bytes to read, we skip
|
// If the read command work with ack_val, but no bytes to read, we skip
|
||||||
// this command, and run next command directly.
|
// this command, and run next command directly.
|
||||||
if (hw_cmd.ack_val == ACK_VAL) {
|
if (hw_cmd.ack_val == I2C_ACK_VAL) {
|
||||||
if (i2c_operation->total_bytes == 0) {
|
if (i2c_operation->total_bytes == 0) {
|
||||||
i2c_master->trans_idx++;
|
i2c_master->trans_idx++;
|
||||||
hw_cmd = i2c_master->i2c_trans.ops[i2c_master->trans_idx].hw_cmd;
|
hw_cmd = i2c_master->i2c_trans.ops[i2c_master->trans_idx].hw_cmd;
|
||||||
@ -365,9 +365,14 @@ static void s_i2c_start_end_command(i2c_master_bus_handle_t i2c_master, i2c_oper
|
|||||||
uint8_t cmd_address = i2c_master->i2c_trans.device_address;
|
uint8_t cmd_address = i2c_master->i2c_trans.device_address;
|
||||||
uint8_t addr_byte = 1;
|
uint8_t addr_byte = 1;
|
||||||
#endif
|
#endif
|
||||||
|
if (i2c_master->i2c_trans.device_address == I2C_DEVICE_ADDRESS_NOT_USED) {
|
||||||
|
// Bypass the address.
|
||||||
|
addr_byte = 0;
|
||||||
|
}
|
||||||
uint8_t addr_write[addr_byte];
|
uint8_t addr_write[addr_byte];
|
||||||
uint8_t addr_read[addr_byte];
|
uint8_t addr_read[addr_byte];
|
||||||
|
|
||||||
|
if (addr_byte != 0) {
|
||||||
addr_write[0] = I2C_ADDRESS_TRANS_WRITE(cmd_address);
|
addr_write[0] = I2C_ADDRESS_TRANS_WRITE(cmd_address);
|
||||||
addr_read[0] = I2C_ADDRESS_TRANS_READ(cmd_address);
|
addr_read[0] = I2C_ADDRESS_TRANS_READ(cmd_address);
|
||||||
#if SOC_I2C_SUPPORT_10BIT_ADDR
|
#if SOC_I2C_SUPPORT_10BIT_ADDR
|
||||||
@ -376,6 +381,7 @@ static void s_i2c_start_end_command(i2c_master_bus_handle_t i2c_master, i2c_oper
|
|||||||
addr_read[1] = i2c_master->i2c_trans.device_address & 0xff;
|
addr_read[1] = i2c_master->i2c_trans.device_address & 0xff;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
portENTER_CRITICAL_SAFE(&i2c_master->base->spinlock);
|
portENTER_CRITICAL_SAFE(&i2c_master->base->spinlock);
|
||||||
i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx);
|
i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx);
|
||||||
@ -1205,8 +1211,8 @@ esp_err_t i2c_master_transmit_receive(i2c_master_dev_handle_t i2c_dev, const uin
|
|||||||
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
||||||
{.hw_cmd = I2C_TRANS_WRITE_COMMAND(i2c_dev->ack_check_disable ? false : true), .data = (uint8_t *)write_buffer, .total_bytes = write_size},
|
{.hw_cmd = I2C_TRANS_WRITE_COMMAND(i2c_dev->ack_check_disable ? false : true), .data = (uint8_t *)write_buffer, .total_bytes = write_size},
|
||||||
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
||||||
{.hw_cmd = I2C_TRANS_READ_COMMAND(ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1},
|
{.hw_cmd = I2C_TRANS_READ_COMMAND(I2C_ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1},
|
||||||
{.hw_cmd = I2C_TRANS_READ_COMMAND(NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1},
|
{.hw_cmd = I2C_TRANS_READ_COMMAND(I2C_NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1},
|
||||||
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1225,8 +1231,8 @@ esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buff
|
|||||||
|
|
||||||
i2c_operation_t i2c_ops[] = {
|
i2c_operation_t i2c_ops[] = {
|
||||||
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
{.hw_cmd = I2C_TRANS_START_COMMAND},
|
||||||
{.hw_cmd = I2C_TRANS_READ_COMMAND(ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1},
|
{.hw_cmd = I2C_TRANS_READ_COMMAND(I2C_ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1},
|
||||||
{.hw_cmd = I2C_TRANS_READ_COMMAND(NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1},
|
{.hw_cmd = I2C_TRANS_READ_COMMAND(I2C_NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1},
|
||||||
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
{.hw_cmd = I2C_TRANS_STOP_COMMAND},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1290,6 +1296,54 @@ esp_err_t i2c_master_probe(i2c_master_bus_handle_t bus_handle, uint16_t address,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t i2c_master_execute_defined_operations(i2c_master_dev_handle_t i2c_dev, i2c_operation_job_t *i2c_operation, size_t operation_list_num, int xfer_timeout_ms)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized");
|
||||||
|
ESP_RETURN_ON_FALSE(i2c_operation != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c operation pointer is invalid");
|
||||||
|
ESP_RETURN_ON_FALSE(operation_list_num <= (SOC_I2C_CMD_REG_NUM), ESP_ERR_INVALID_ARG, TAG, "i2c command list cannot contain so many commands");
|
||||||
|
|
||||||
|
i2c_operation_t i2c_ops[operation_list_num] = {};
|
||||||
|
for (int i = 0; i < operation_list_num; i++) {
|
||||||
|
switch (i2c_operation[i].command) {
|
||||||
|
case I2C_MASTER_CMD_START:
|
||||||
|
i2c_ops[i].hw_cmd.op_code = I2C_LL_CMD_RESTART;
|
||||||
|
break;
|
||||||
|
case I2C_MASTER_CMD_WRITE:
|
||||||
|
i2c_ops[i].hw_cmd.op_code = I2C_LL_CMD_WRITE;
|
||||||
|
i2c_ops[i].hw_cmd.ack_en = i2c_operation[i].write.ack_check;
|
||||||
|
i2c_ops[i].data = i2c_operation[i].write.data;
|
||||||
|
i2c_ops[i].total_bytes = i2c_operation[i].write.total_bytes;
|
||||||
|
break;
|
||||||
|
case I2C_MASTER_CMD_READ:
|
||||||
|
i2c_ops[i].hw_cmd.op_code = I2C_LL_CMD_READ;
|
||||||
|
i2c_ops[i].hw_cmd.ack_val = i2c_operation[i].read.ack_value;
|
||||||
|
i2c_ops[i].data = i2c_operation[i].read.data;
|
||||||
|
i2c_ops[i].total_bytes = i2c_operation[i].read.total_bytes;
|
||||||
|
// Add check: If current command is READ and the next command is STOP, ack_value must be NACK
|
||||||
|
if (i + 1 < operation_list_num && i2c_operation[i + 1].command == I2C_MASTER_CMD_STOP) {
|
||||||
|
if (i2c_operation[i].read.ack_value != I2C_NACK_VAL) {
|
||||||
|
ESP_LOGE(TAG, "ack_value must be NACK (1) when the next command of READ is STOP.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case I2C_MASTER_CMD_STOP:
|
||||||
|
i2c_ops[i].hw_cmd.op_code = I2C_LL_CMD_STOP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid command.");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_dev->master_bus->async_trans == false) {
|
||||||
|
ESP_RETURN_ON_ERROR(s_i2c_synchronous_transaction(i2c_dev, i2c_ops, operation_list_num, xfer_timeout_ms), TAG, "I2C transaction failed");
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_ERROR(s_i2c_asynchronous_transaction(i2c_dev, i2c_ops, operation_list_num, xfer_timeout_ms), TAG, "I2C transaction failed");
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, const i2c_master_event_callbacks_t *cbs, void *user_data)
|
esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, const i2c_master_event_callbacks_t *cbs, void *user_data)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized");
|
ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized");
|
||||||
|
@ -63,9 +63,6 @@ extern "C" {
|
|||||||
#define I2C_PM_LOCK_NAME_LEN_MAX 16
|
#define I2C_PM_LOCK_NAME_LEN_MAX 16
|
||||||
#define I2C_STATIC_OPERATION_ARRAY_MAX 6
|
#define I2C_STATIC_OPERATION_ARRAY_MAX 6
|
||||||
|
|
||||||
#define ACK_VAL 0
|
|
||||||
#define NACK_VAL 1
|
|
||||||
|
|
||||||
#define I2C_TRANS_READ_COMMAND(ack_value) {.ack_val = (ack_value), .op_code = I2C_LL_CMD_READ}
|
#define I2C_TRANS_READ_COMMAND(ack_value) {.ack_val = (ack_value), .op_code = I2C_LL_CMD_READ}
|
||||||
#define I2C_TRANS_WRITE_COMMAND(ack_check) {.ack_en = (ack_check), .op_code = I2C_LL_CMD_WRITE}
|
#define I2C_TRANS_WRITE_COMMAND(ack_check) {.ack_en = (ack_check), .op_code = I2C_LL_CMD_WRITE}
|
||||||
#define I2C_TRANS_STOP_COMMAND {.op_code = I2C_LL_CMD_STOP}
|
#define I2C_TRANS_STOP_COMMAND {.op_code = I2C_LL_CMD_STOP}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -39,12 +39,14 @@ typedef struct {
|
|||||||
} flags; /*!< I2C master config flags */
|
} flags; /*!< I2C master config flags */
|
||||||
} i2c_master_bus_config_t;
|
} i2c_master_bus_config_t;
|
||||||
|
|
||||||
|
#define I2C_DEVICE_ADDRESS_NOT_USED (0xffff) /*!< Skip carry address bit in driver transmit and receive */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief I2C device configuration
|
* @brief I2C device configuration
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
i2c_addr_bit_len_t dev_addr_length; /*!< Select the address length of the slave device. */
|
i2c_addr_bit_len_t dev_addr_length; /*!< Select the address length of the slave device. */
|
||||||
uint16_t device_address; /*!< I2C device raw address. (The 7/10 bit address without read/write bit) */
|
uint16_t device_address; /*!< I2C device raw address. (The 7/10 bit address without read/write bit). Macro I2C_DEVICE_ADDRESS_NOT_USED (0xFFFF) stands for skip the address config inside driver. */
|
||||||
uint32_t scl_speed_hz; /*!< I2C SCL line frequency. */
|
uint32_t scl_speed_hz; /*!< I2C SCL line frequency. */
|
||||||
uint32_t scl_wait_us; /*!< Timeout value. (unit: us). Please note this value should not be so small that it can handle stretch/disturbance properly. If 0 is set, that means use the default reg value*/
|
uint32_t scl_wait_us; /*!< Timeout value. (unit: us). Please note this value should not be so small that it can handle stretch/disturbance properly. If 0 is set, that means use the default reg value*/
|
||||||
struct {
|
struct {
|
||||||
@ -52,6 +54,38 @@ typedef struct {
|
|||||||
} flags; /*!< I2C device config flags */
|
} flags; /*!< I2C device config flags */
|
||||||
} i2c_device_config_t;
|
} i2c_device_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure representing an I2C operation job
|
||||||
|
*
|
||||||
|
* This structure is used to define individual I2C operations (write or read)
|
||||||
|
* within a sequence of I2C master transactions.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
i2c_master_command_t command; /**< I2C command indicating the type of operation (START, WRITE, READ, or STOP) */
|
||||||
|
union {
|
||||||
|
/**
|
||||||
|
* @brief Structure for WRITE command
|
||||||
|
*
|
||||||
|
* Used when the `command` is set to `I2C_MASTER_CMD_WRITE`.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
bool ack_check; /**< Whether to enable ACK check during WRITE operation */
|
||||||
|
uint8_t *data; /**< Pointer to the data to be written */
|
||||||
|
size_t total_bytes; /**< Total number of bytes to write */
|
||||||
|
} write;
|
||||||
|
/**
|
||||||
|
* @brief Structure for READ command
|
||||||
|
*
|
||||||
|
* Used when the `command` is set to `I2C_MASTER_CMD_READ`.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
i2c_ack_value_t ack_value; /**< ACK value to send after the read (ACK or NACK) */
|
||||||
|
uint8_t *data; /**< Pointer to the buffer for storing the data read from the bus */
|
||||||
|
size_t total_bytes; /**< Total number of bytes to read */
|
||||||
|
} read;
|
||||||
|
};
|
||||||
|
} i2c_operation_job_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief I2C master transmit buffer information structure
|
* @brief I2C master transmit buffer information structure
|
||||||
*/
|
*/
|
||||||
@ -218,6 +252,30 @@ esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buff
|
|||||||
*/
|
*/
|
||||||
esp_err_t i2c_master_probe(i2c_master_bus_handle_t bus_handle, uint16_t address, int xfer_timeout_ms);
|
esp_err_t i2c_master_probe(i2c_master_bus_handle_t bus_handle, uint16_t address, int xfer_timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a series of pre-defined I2C operations.
|
||||||
|
*
|
||||||
|
* This function processes a list of I2C operations, such as start, write, read, and stop,
|
||||||
|
* according to the user-defined `i2c_operation_job_t` array. It performs these operations
|
||||||
|
* sequentially on the specified I2C master device.
|
||||||
|
*
|
||||||
|
* @param[in] i2c_dev Handle to the I2C master device.
|
||||||
|
* @param[in] i2c_operation Pointer to an array of user-defined I2C operation jobs.
|
||||||
|
* Each job specifies a command and associated parameters.
|
||||||
|
* @param[in] operation_list_num The number of operations in the `i2c_operation` array.
|
||||||
|
* @param[in] xfer_timeout_ms Timeout for the transaction, in milliseconds.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Transaction completed successfully.
|
||||||
|
* - ESP_ERR_INVALID_ARG: One or more arguments are invalid.
|
||||||
|
* - ESP_ERR_TIMEOUT: Transaction timed out.
|
||||||
|
* - ESP_FAIL: Other error during transaction.
|
||||||
|
*
|
||||||
|
* @note The `ack_value` field in the READ operation must be set to `I2C_NACK_VAL` if the next
|
||||||
|
* operation is a STOP command.
|
||||||
|
*/
|
||||||
|
esp_err_t i2c_master_execute_defined_operations(i2c_master_dev_handle_t i2c_dev, i2c_operation_job_t *i2c_operation, size_t operation_list_num, int xfer_timeout_ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register I2C transaction callbacks for a master device
|
* @brief Register I2C transaction callbacks for a master device
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -45,6 +45,29 @@ typedef enum {
|
|||||||
I2C_EVENT_TIMEOUT, /*!< i2c bus timeout */
|
I2C_EVENT_TIMEOUT, /*!< i2c bus timeout */
|
||||||
} i2c_master_event_t;
|
} i2c_master_event_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enum for I2C master commands
|
||||||
|
*
|
||||||
|
* These commands are used to define the I2C master operations.
|
||||||
|
* They correspond to hardware-level commands supported by the I2C peripheral.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
I2C_MASTER_CMD_START, /**< Start or Restart condition */
|
||||||
|
I2C_MASTER_CMD_WRITE, /**< Write operation */
|
||||||
|
I2C_MASTER_CMD_READ, /**< Read operation */
|
||||||
|
I2C_MASTER_CMD_STOP, /**< Stop condition */
|
||||||
|
} i2c_master_command_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enum for I2C master ACK values
|
||||||
|
*
|
||||||
|
* These values define the acknowledgment (ACK) behavior during read operations.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
I2C_ACK_VAL = 0, /**< Acknowledge (ACK) signal */
|
||||||
|
I2C_NACK_VAL = 1, /**< Not Acknowledge (NACK) signal */
|
||||||
|
} __attribute__((packed)) i2c_ack_value_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Type of I2C master bus handle
|
* @brief Type of I2C master bus handle
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user