/* * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include #include #include "esp_err.h" #include "driver/i2c_types.h" #include "hal/i2c_hal.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" #include "freertos/ringbuf.h" #include "esp_pm.h" #ifdef __cplusplus extern "C" { #endif #if CONFIG_I2C_ISR_IRAM_SAFE #define I2C_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) #else #define I2C_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT) #endif // I2C driver object is per-mode, the interrupt source is shared between modes #if CONFIG_I2C_ISR_IRAM_SAFE #define I2C_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LOWMED) #else #define I2C_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED) #endif #define I2C_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED #define I2C_PM_LOCK_NAME_LEN_MAX 16 #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_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_START_COMMAND {.op_code = I2C_LL_CMD_RESTART} typedef struct i2c_bus_t i2c_bus_t; typedef struct i2c_master_bus_t i2c_master_bus_t; typedef struct i2c_bus_t *i2c_bus_handle_t; typedef struct i2c_master_dev_t i2c_master_dev_t; typedef enum { I2C_BUS_MODE_MASTER = 0, I2C_BUS_MODE_SLAVE = 1, } i2c_bus_mode_t; typedef enum { I2C_SLAVE_FIFO = 0, I2C_SLAVE_NONFIFO = 1, } i2c_slave_fifo_mode_t; enum { I2C_TRANS_QUEUE_READY, I2C_TRANS_QUEUE_PROGRESS, I2C_TRANS_QUEUE_COMPLETE, I2C_TRANS_QUEUE_MAX, }; typedef struct { i2c_ll_hw_cmd_t hw_cmd; // I2C command, defined by hardware uint8_t *data; // Pointer to data uint16_t bytes_used; // I2C data has been used size_t total_bytes; // Total bytes to be transferred } i2c_operation_t; typedef struct { uint32_t device_address; // Address of I2C device i2c_operation_t *ops; // Pointer to I2C operation structure size_t cmd_count; // Record how many I2C hardware commands in one transaction } i2c_transaction_t; struct i2c_bus_t { i2c_port_num_t port_num; // Port(Bus) ID, index from 0 portMUX_TYPE spinlock; // To protect pre-group register level concurrency access i2c_hal_context_t hal; // Hal layer for each port(bus) i2c_clock_source_t clk_src; // Record the port clock source uint32_t clk_src_freq_hz; // Record the clock source frequency int sda_num; // SDA pin number int scl_num; // SCL pin number bool pull_up_enable; // Enable pull-ups intr_handle_t intr_handle; // I2C interrupt handle esp_pm_lock_handle_t pm_lock; // power manange lock #if CONFIG_PM_ENABLE char pm_lock_name[I2C_PM_LOCK_NAME_LEN_MAX]; // pm lock name #endif i2c_bus_mode_t bus_mode; // I2C bus mode }; typedef struct i2c_master_device_list { i2c_master_dev_t *device; SLIST_ENTRY(i2c_master_device_list) next; } i2c_master_device_list_t; struct i2c_master_bus_t { i2c_bus_t *base; // bus base class SemaphoreHandle_t bus_lock_mux; // semaphore to lock bus process int cmd_idx; //record current command index, for master mode _Atomic i2c_master_status_t status; // record current command status, for master mode i2c_master_event_t event; // record current i2c bus event int rx_cnt; // record current read index, for master mode i2c_transaction_t i2c_trans; // Pointer to I2C transfer structure i2c_operation_t i2c_ops[I2C_STATIC_OPERATION_ARRAY_MAX]; // I2C operation array _Atomic uint16_t trans_idx; // Index of I2C transaction command. SemaphoreHandle_t cmd_semphr; // Semaphore between task and interrupt, using for synchronizing ISR and I2C task. uint32_t read_buf_pos; // Read buffer position bool contains_read; // Whether command array includes read operation, true: yes, otherwise, false. uint32_t read_len_static; // Read static buffer length uint32_t w_r_size; // The size send/receive last time. bool trans_over_buffer; // Data length is more than hardware fifo length, needs interrupt. bool asnyc_trans; // asynchronous transaction, true after callback is installed. volatile bool trans_done; // transaction command finish SLIST_HEAD(i2c_master_device_list_head, i2c_master_device_list) device_list; // I2C device (instance) list // asnyc trans members bool async_break; // break transaction loop flag. i2c_addr_bit_len_t addr_10bits_bus; // Slave address is 10 bits. size_t queue_size; // I2C transaction queue size. size_t num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue size_t num_trans_inqueue; // Indicates the number of transaction in queue transaction. void* queues_storage; // storage of transaction queues bool sent_all; // true if the queue transaction is sent bool in_progress; // true if current transaction is in progress bool trans_finish; // true if current command has been sent out. bool queue_trans; // true if current transaction is in queue bool new_queue; // true if allow a new queue transaction size_t index; // transaction index QueueHandle_t trans_queues[I2C_TRANS_QUEUE_MAX]; // transaction queues. StaticQueue_t trans_queue_structs[I2C_TRANS_QUEUE_MAX]; // memory to store the static structure for trans_queues i2c_operation_t **i2c_anyc_ops; // pointer to asynchronous operation. uint8_t **anyc_write_buffer; // pointer to asynchronous write buffer. i2c_transaction_t i2c_trans_pool[]; // I2C transaction pool. }; struct i2c_master_dev_t { i2c_master_bus_t *master_bus; // I2C master bus base class uint16_t device_address; // I2C device address uint32_t scl_speed_hz; // SCL clock frequency i2c_addr_bit_len_t addr_10bits; // Whether I2C device is a 10-bits address device. i2c_master_callback_t on_trans_done; // I2C master transaction done callback. void *user_ctx; // Callback user context }; /** * @brief Acquire I2C bus handle * * @param port_num I2C port number. * @return * - ESP_OK: Acquire bus handle successfully. * - ESP_ERR_INVALID_ARG: Argument error. * - ESP_ERR_INVALID_STATE: Acquire bus invalid state because bus has already acquired. */ esp_err_t i2c_acquire_bus_handle(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode); /** * @brief Release I2C bus handle * * @param i2c_bus I2C bus handle, returned from `i2c_acquire_bus_handle` * @return ESP_OK: If release successfully * ESP_ERR_INVALID_STATE: Release bus failed because same bus has been required several times. * Otherwise: Other reasons. */ esp_err_t i2c_release_bus_handle(i2c_bus_handle_t i2c_bus); /** * @brief Set clock source for I2C peripheral * * @param handle I2C bus handle * @param clk_src Clock source * @return * - ESP_OK: Set clock source successfully * - ESP_ERR_NOT_SUPPORTED: Set clock source failed because the clk_src is not supported * - ESP_ERR_INVALID_STATE: Set clock source failed because the clk_src is different from other I2C controller * - ESP_FAIL: Set clock source failed because of other error */ esp_err_t i2c_select_periph_clock(i2c_bus_handle_t handle, i2c_clock_source_t clk_src); /** * @brief Set I2C SCL/SDA pins * * @param handle I2C bus handle * @return * - ESP_OK: I2C set SCL/SDA pins successfully. * - ESP_ERR_INVALID_ARG: Argument error. * - Otherwise: Set SCL/SDA IOs error. */ esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle); /** * @brief Check whether I2C bus is occupied * * @param port_num I2C port number. * @return true: occupied, otherwise, false. */ bool i2c_bus_occupied(i2c_port_num_t port_num); #ifdef __cplusplus } #endif