2023-08-03 00:29:44 -04:00
/*
* SPDX - FileCopyrightText : 2023 Espressif Systems ( Shanghai ) CO LTD
*
* SPDX - License - Identifier : Apache - 2.0
*/
# include <string.h>
# include <sys/param.h>
# include <sys/lock.h>
# include "sdkconfig.h"
# include "esp_types.h"
# include "esp_attr.h"
# include "esp_check.h"
# if CONFIG_I2C_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
# define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
# endif
# include "esp_log.h"
# include "esp_intr_alloc.h"
# include "freertos/FreeRTOS.h"
# include "freertos/semphr.h"
# include "soc/i2c_periph.h"
# include "esp_private/periph_ctrl.h"
# include "esp_private/esp_clk.h"
# include "esp_rom_gpio.h"
# include "driver/i2c_master.h"
# include "i2c_private.h"
# include "driver/gpio.h"
# include "clk_ctrl_os.h"
# include "hal/i2c_types.h"
# include "hal/i2c_hal.h"
# include "hal/gpio_hal.h"
# include "esp_memory_utils.h"
# include "freertos/idf_additions.h"
static const char * TAG = " i2c.master " ;
# define DIM(array) (sizeof(array) / sizeof(*array))
# define I2C_ADDRESS_TRANS_WRITE(device_address) (((device_address) << 1) | 0)
# define I2C_ADDRESS_TRANS_READ(device_address) (((device_address) << 1) | 1)
static esp_err_t s_i2c_master_clear_bus ( i2c_bus_handle_t handle )
{
# if !SOC_I2C_SUPPORT_HW_FSM_RST
const int scl_half_period = 5 ; // use standard 100kHz data rate
int i = 0 ;
gpio_set_direction ( handle - > scl_num , GPIO_MODE_OUTPUT_OD ) ;
gpio_set_direction ( handle - > sda_num , GPIO_MODE_INPUT_OUTPUT_OD ) ;
// If a SLAVE device was in a read operation when the bus was interrupted, the SLAVE device is controlling SDA.
// The only bit during the 9 clock cycles of a READ byte the MASTER(ESP32) is guaranteed control over is during the ACK bit
// period. If the slave is sending a stream of ZERO bytes, it will only release SDA during the ACK bit period.
// So, this reset code needs to synchronous the bit stream with, Either, the ACK bit, Or a 1 bit to correctly generate
// a STOP condition.
gpio_set_level ( handle - > scl_num , 0 ) ;
gpio_set_level ( handle - > sda_num , 1 ) ;
esp_rom_delay_us ( scl_half_period ) ;
while ( ! gpio_get_level ( handle - > sda_num ) & & ( i + + < 9 ) ) {
gpio_set_level ( handle - > scl_num , 1 ) ;
esp_rom_delay_us ( scl_half_period ) ;
gpio_set_level ( handle - > scl_num , 0 ) ;
esp_rom_delay_us ( scl_half_period ) ;
}
gpio_set_level ( handle - > sda_num , 0 ) ; // setup for STOP
gpio_set_level ( handle - > scl_num , 1 ) ;
esp_rom_delay_us ( scl_half_period ) ;
gpio_set_level ( handle - > sda_num , 1 ) ; // STOP, SDA low -> high while SCL is HIGH
i2c_common_set_pins ( handle ) ;
# else
i2c_hal_context_t * hal = & handle - > hal ;
i2c_ll_master_clr_bus ( hal - > dev , I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT ) ;
# endif
return ESP_OK ;
}
/**
* @ brief Reset I2C hardware fsm .
* 1. Store filter and timing stuff ( they are I2C hardware configure stuff )
* so , the configuration is same after reset .
*
* @ param [ in ] i2c_master I2C master handle
*/
static esp_err_t s_i2c_hw_fsm_reset ( i2c_master_bus_handle_t i2c_master )
{
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
i2c_hal_timing_config_t timing_config ;
uint8_t filter_cfg ;
i2c_hal_get_timing_config ( hal , & timing_config ) ;
i2c_ll_master_get_filter ( hal - > dev , & filter_cfg ) ;
//to reset the I2C hw module, we need re-enable the hw
s_i2c_master_clear_bus ( i2c_master - > base ) ;
periph_module_disable ( i2c_periph_signal [ i2c_master - > base - > port_num ] . module ) ;
periph_module_enable ( i2c_periph_signal [ i2c_master - > base - > port_num ] . module ) ;
i2c_hal_master_init ( hal ) ;
i2c_ll_disable_intr_mask ( hal - > dev , I2C_LL_INTR_MASK ) ;
i2c_ll_clear_intr_mask ( hal - > dev , I2C_LL_INTR_MASK ) ;
i2c_hal_set_timing_config ( hal , & timing_config ) ;
i2c_ll_master_set_filter ( hal - > dev , filter_cfg ) ;
return ESP_OK ;
}
//////////////////////////////////////I2C operation functions////////////////////////////////////////////
/**
* @ brief This function is used to send I2C write command , which is divided in two parts .
* - 1. If write buffer is smaller than hardware fifo , it can be sent out in one single time ,
* so the hardware command ( step ) is simply like start ( 1 ) - > write ( 2 ) - > end ( 3 )
* - 2. If write buffer is larger than hardware fifo , it cannot be sent out in one time , so it needs
* to be separated in to different transactions by interrupt . In this time , the hardware command ( step )
* simply looks like start ( 1 ) - > write_part ( 2 ) - - interrupt - - . . . - - write ( 1 ) - > end ( 2 ) .
*
* @ param [ in ] i2c_master I2C master handle
* @ param [ in ] i2c_operation Pointer to I2C trans operation structure .
* @ param [ in ] fifo_fill will be populated with the number of bytes written to the harware FIFO by this function
* @ param [ in ] address_fill I2C device address with read or write information .
*/
static bool s_i2c_write_command ( i2c_master_bus_handle_t i2c_master , i2c_operation_t * i2c_operation , uint8_t * fifo_fill , uint8_t * address_fill , BaseType_t * do_yield )
{
i2c_master - > async_break = false ;
const size_t remaining_bytes = i2c_operation - > total_bytes - i2c_operation - > bytes_used ;
const i2c_ll_hw_cmd_t hw_end_cmd = {
. op_code = I2C_LL_CMD_END
} ;
uint8_t * write_pr = NULL ;
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
i2c_bus_handle_t handle = i2c_master - > base ;
i2c_ll_hw_cmd_t hw_cmd = i2c_operation - > hw_cmd ;
uint8_t data_fill = 0 ;
// data_fill refers to the length to fill the data
data_fill = MIN ( remaining_bytes , SOC_I2C_FIFO_LEN - * address_fill ) ;
write_pr = i2c_operation - > data + i2c_operation - > bytes_used ;
i2c_operation - > bytes_used + = data_fill ;
hw_cmd . byte_num = data_fill + * address_fill ;
* address_fill = 0 ;
atomic_store ( & i2c_master - > status , I2C_STATUS_WRITE ) ;
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_ll_write_txfifo ( hal - > dev , write_pr , data_fill ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_cmd , i2c_master - > cmd_idx ) ;
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_master - > w_r_size = data_fill ;
# if SOC_I2C_STOP_INDEPENDENT
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_end_cmd , i2c_master - > cmd_idx + 1 ) ;
i2c_master - > cmd_idx = 0 ;
if ( i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx ] . total_bytes = = i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx ] . bytes_used ) {
i2c_master - > i2c_trans . cmd_count - - ;
i2c_master - > trans_idx + + ;
}
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
if ( i2c_master - > asnyc_trans = = false ) {
i2c_hal_master_trans_start ( hal ) ;
} else {
i2c_master - > async_break = true ;
}
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
# else
// If data cannot be sent in one time, send data out. Otherwise, continue configuring command.
if ( ( remaining_bytes - data_fill ) ! = 0 ) {
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_end_cmd , i2c_master - > cmd_idx + 1 ) ;
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_master - > cmd_idx = 0 ;
if ( i2c_master - > asnyc_trans = = false ) {
i2c_hal_master_trans_start ( hal ) ;
} else {
i2c_master - > async_break = true ;
}
} else {
i2c_master - > cmd_idx + + ;
i2c_master - > trans_idx + + ;
i2c_master - > i2c_trans . cmd_count - - ;
if ( i2c_master - > asnyc_trans = = false ) {
if ( xPortInIsrContext ( ) ) {
xSemaphoreGiveFromISR ( i2c_master - > cmd_semphr , do_yield ) ;
} else {
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
}
}
}
# endif
* fifo_fill = data_fill ;
return i2c_master - > async_break ;
}
/**
* @ brief This function is used to send I2C read command , which is divided in three parts .
* - 1. If read buffer is smaller than hardware fifo , it can be sent out in one single time ,
* so the hardware command ( step ) is simply like start ( 1 ) - > read_ack ( 2 ) - > read_nack ( 3 ) - > end ( 4 )
* - 2. If read buffer is larger than hardware fifo , it cannot be sent out in one time , so it needs
* to be separated in to different transactions by interrupt . In this time , the hardware command ( step )
* simply looks like start ( 1 ) - > read_part ( 2 ) - - interrupt - - . . . - - read ( 1 ) - > end ( 2 ) .
* - 3. If only one byte is waiting to be read . only send nack command . like start ( 1 ) - > read_nack ( 2 ) - > end ( 3 )
*
* @ param [ in ] i2c_master I2C master handle
* @ param [ in ] i2c_operation Pointer to I2C trans operation structure .
* @ param [ out ] fifo_fill Pointer to read buffer length
*/
static bool s_i2c_read_command ( i2c_master_bus_handle_t i2c_master , i2c_operation_t * i2c_operation , uint8_t * fifo_fill , BaseType_t * do_yield )
{
i2c_master - > async_break = false ;
const size_t remaining_bytes = i2c_operation - > total_bytes - i2c_operation - > bytes_used ;
const i2c_ll_hw_cmd_t hw_end_cmd = {
. op_code = I2C_LL_CMD_END
} ;
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
i2c_bus_handle_t handle = i2c_master - > base ;
i2c_ll_hw_cmd_t hw_cmd = i2c_operation - > hw_cmd ;
* fifo_fill = MIN ( remaining_bytes , SOC_I2C_FIFO_LEN - i2c_master - > read_len_static ) ;
i2c_master - > rx_cnt = * fifo_fill ;
hw_cmd . byte_num = * fifo_fill ;
i2c_master - > contains_read = true ;
# if !SOC_I2C_STOP_INDEPENDENT
if ( remaining_bytes < SOC_I2C_FIFO_LEN - 1 ) {
if ( i2c_operation - > hw_cmd . ack_val = = ACK_VAL ) {
if ( remaining_bytes ! = 0 ) {
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 - > cmd_idx + + ;
}
i2c_master - > read_buf_pos = i2c_master - > trans_idx ;
} else {
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
}
i2c_master - > trans_idx + + ;
i2c_master - > i2c_trans . cmd_count - - ;
if ( i2c_master - > asnyc_trans = = false ) {
if ( xPortInIsrContext ( ) ) {
xSemaphoreGiveFromISR ( i2c_master - > cmd_semphr , do_yield ) ;
} else {
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
}
}
} else {
atomic_store ( & i2c_master - > status , I2C_STATUS_READ ) ;
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_cmd , i2c_master - > cmd_idx ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_end_cmd , i2c_master - > cmd_idx + 1 ) ;
if ( i2c_master - > asnyc_trans = = false ) {
i2c_hal_master_trans_start ( hal ) ;
} else {
i2c_master - > async_break = true ;
}
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
}
# else
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_cmd , i2c_master - > cmd_idx ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_end_cmd , i2c_master - > cmd_idx + 1 ) ;
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
i2c_master - > status = I2C_STATUS_READ ;
portENTER_CRITICAL_SAFE ( & handle - > spinlock ) ;
if ( i2c_master - > asnyc_trans = = false ) {
i2c_hal_master_trans_start ( hal ) ;
} else {
i2c_master - > async_break = true ;
}
portEXIT_CRITICAL_SAFE ( & handle - > spinlock ) ;
# endif
return i2c_master - > async_break ;
}
/**
* @ brief This function is used to send I2C start or stop command , which is divided in two parts .
* If start command is accepted , a write address command must be followed . So prepared one address
* data here . Send with write or read command .
*
* @ param [ in ] i2c_master I2C master handle
* @ param [ in ] i2c_operation Pointer to I2C trans operation structure .
* @ param [ in ] address_fill I2C device address with read or write information .
*/
static void s_i2c_start_end_command ( i2c_master_bus_handle_t i2c_master , i2c_operation_t * i2c_operation , uint8_t * address_fill , BaseType_t * do_yield )
{
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
i2c_ll_hw_cmd_t hw_cmd = i2c_operation - > hw_cmd ;
# if SOC_I2C_SUPPORT_10BIT_ADDR
uint8_t cmd_address = ( i2c_master - > addr_10bits_bus = = I2C_ADDR_BIT_LEN_7 ) ? i2c_master - > i2c_trans . device_address : ( ( i2c_master - > i2c_trans . device_address > > 8 ) | 0x78 ) ;
uint8_t addr_byte = ( i2c_master - > addr_10bits_bus = = I2C_ADDR_BIT_LEN_7 ) ? 1 : 2 ;
# else
uint8_t cmd_address = i2c_master - > i2c_trans . device_address ;
uint8_t addr_byte = 1 ;
# endif
uint8_t addr_write [ addr_byte ] ;
uint8_t addr_read [ addr_byte ] ;
addr_write [ 0 ] = I2C_ADDRESS_TRANS_WRITE ( cmd_address ) ;
addr_read [ 0 ] = I2C_ADDRESS_TRANS_READ ( cmd_address ) ;
# if SOC_I2C_SUPPORT_10BIT_ADDR
if ( i2c_master - > addr_10bits_bus = = I2C_ADDR_BIT_LEN_10 ) {
addr_write [ 1 ] = i2c_master - > i2c_trans . device_address & 0xff ;
addr_read [ 1 ] = i2c_master - > i2c_trans . device_address & 0xff ;
}
# endif
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
if ( i2c_operation - > hw_cmd . op_code = = I2C_LL_CMD_RESTART ) {
2023-08-31 03:06:46 -04:00
i2c_operation_t next_transaction = i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx + 1 ] ;
2023-08-03 00:29:44 -04:00
2023-08-31 03:06:46 -04:00
if ( next_transaction . hw_cmd . op_code = = I2C_LL_CMD_READ ) {
2023-08-03 00:29:44 -04:00
# if SOC_I2C_SUPPORT_10BIT_ADDR
if ( i2c_master - > addr_10bits_bus = = I2C_ADDR_BIT_LEN_10 ) {
i2c_ll_hw_cmd_t hw_write_cmd = {
. ack_en = false ,
. op_code = I2C_LL_CMD_WRITE ,
. byte_num = 2 ,
} ;
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_write_txfifo ( hal - > dev , addr_write , sizeof ( addr_write ) ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_write_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
const i2c_ll_hw_cmd_t hw_start_cmd = {
. op_code = I2C_LL_CMD_RESTART ,
} ;
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_start_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
}
# endif
i2c_ll_hw_cmd_t hw_write_cmd = {
. ack_en = false ,
. op_code = I2C_LL_CMD_WRITE ,
. byte_num = 1 ,
} ;
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_write_txfifo ( hal - > dev , addr_read , sizeof ( addr_read ) ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_write_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
2023-08-31 03:06:46 -04:00
} else if ( next_transaction . hw_cmd . op_code = = I2C_LL_CMD_STOP ) {
i2c_ll_hw_cmd_t hw_write_cmd = {
. ack_en = true ,
. op_code = I2C_LL_CMD_WRITE ,
. byte_num = 1 ,
} ;
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_write_txfifo ( hal - > dev , addr_write , sizeof ( addr_write ) ) ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_write_cmd , i2c_master - > cmd_idx ) ;
i2c_master - > cmd_idx + + ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
2023-08-03 00:29:44 -04:00
} else {
/* The next transaction is a WRITE, we can merge the device address byte
* with the next write command . No need to write the ` cmd_reg ` as the next
* command will do it for us , we only need to add the device address byte
* in the TX FIFO . */
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_ll_write_txfifo ( hal - > dev , addr_write , sizeof ( addr_write ) ) ;
* address_fill + = sizeof ( addr_write ) ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
}
if ( i2c_master - > asnyc_trans = = false ) {
if ( xPortInIsrContext ( ) ) {
xSemaphoreGiveFromISR ( i2c_master - > cmd_semphr , do_yield ) ;
} else {
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
}
}
} else {
atomic_store ( & i2c_master - > status , I2C_STATUS_STOP ) ;
}
portENTER_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
i2c_master - > trans_idx + + ;
i2c_master - > i2c_trans . cmd_count - - ;
portEXIT_CRITICAL_SAFE ( & i2c_master - > base - > spinlock ) ;
}
static void s_i2c_send_commands ( i2c_master_bus_handle_t i2c_master , TickType_t ticks_to_wait )
{
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
uint8_t fifo_fill = 0 ;
uint8_t address_fill = 0 ;
while ( i2c_master - > i2c_trans . cmd_count ) {
if ( xSemaphoreTake ( i2c_master - > cmd_semphr , ticks_to_wait ) ! = pdTRUE ) {
// Software timeout, clear the command link and finish this transaction.
i2c_master - > cmd_idx = 0 ;
i2c_master - > trans_idx = 0 ;
return ;
}
if ( i2c_master - > status = = I2C_STATUS_TIMEOUT ) {
s_i2c_hw_fsm_reset ( i2c_master ) ;
i2c_master - > cmd_idx = 0 ;
i2c_master - > trans_idx = 0 ;
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
return ;
}
if ( i2c_master - > status = = I2C_STATUS_ACK_ERROR ) {
const i2c_ll_hw_cmd_t hw_stop_cmd = {
. op_code = I2C_LL_CMD_STOP ,
} ;
i2c_ll_master_write_cmd_reg ( hal - > dev , hw_stop_cmd , 0 ) ;
i2c_hal_master_trans_start ( hal ) ;
return ;
}
i2c_operation_t * i2c_operation = & i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx ] ;
if ( i2c_operation - > hw_cmd . op_code = = I2C_LL_CMD_WRITE ) {
s_i2c_write_command ( i2c_master , i2c_operation , & fifo_fill , & address_fill , NULL ) ;
} else if ( i2c_operation - > hw_cmd . op_code = = I2C_LL_CMD_READ ) {
s_i2c_read_command ( i2c_master , i2c_operation , & fifo_fill , NULL ) ;
} else {
s_i2c_start_end_command ( i2c_master , i2c_operation , & address_fill , NULL ) ;
}
}
i2c_hal_master_trans_start ( hal ) ;
// For blocking implementation, we must wait `done` interrupt to update the status.
while ( i2c_master - > trans_done = = false ) { } ;
2023-08-31 03:06:46 -04:00
if ( i2c_master - > status ! = I2C_STATUS_ACK_ERROR & & i2c_master - > status ! = I2C_STATUS_TIMEOUT ) {
atomic_store ( & i2c_master - > status , I2C_STATUS_DONE ) ;
}
2023-08-03 00:29:44 -04:00
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
}
static void s_i2c_send_command_async ( i2c_master_bus_handle_t i2c_master , BaseType_t * do_yield )
{
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
uint8_t address_fill = 0 ;
bool needs_start = false ;
if ( i2c_master - > i2c_trans . cmd_count = = 0 ) {
// No extra commands.
i2c_master - > trans_finish = true ;
i2c_master - > in_progress = false ;
if ( i2c_master - > queue_trans ) {
i2c_master - > new_queue = true ;
xQueueSendFromISR ( i2c_master - > trans_queues [ I2C_TRANS_QUEUE_COMPLETE ] , & i2c_master - > i2c_trans , do_yield ) ;
}
i2c_master - > sent_all = true ;
return ;
}
while ( i2c_ll_is_bus_busy ( hal - > dev ) ) { }
while ( i2c_master - > i2c_trans . cmd_count & & ! needs_start ) {
i2c_master - > in_progress = true ;
i2c_master - > sent_all = false ;
i2c_operation_t * i2c_operation = & i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx ] ;
uint8_t fifo_fill = 0 ;
if ( i2c_operation - > hw_cmd . op_code = = I2C_LL_CMD_WRITE ) {
needs_start = s_i2c_write_command ( i2c_master , i2c_operation , & fifo_fill , & address_fill , do_yield ) ;
} else if ( i2c_operation - > hw_cmd . op_code = = I2C_LL_CMD_READ ) {
needs_start = s_i2c_read_command ( i2c_master , i2c_operation , & fifo_fill , do_yield ) ;
} else {
s_i2c_start_end_command ( i2c_master , i2c_operation , & address_fill , do_yield ) ;
}
}
i2c_hal_master_trans_start ( hal ) ;
}
static esp_err_t s_i2c_transaction_start ( i2c_master_dev_handle_t i2c_dev , int xfer_timeout_ms )
{
i2c_master_bus_handle_t i2c_master = i2c_dev - > master_bus ;
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
TickType_t ticks_to_wait = ( xfer_timeout_ms = = - 1 ) ? portMAX_DELAY : pdMS_TO_TICKS ( xfer_timeout_ms ) ;
// Sometimes when the FSM get stuck, the ACK_ERR interrupt will occur endlessly until we reset the FSM and clear bus.
esp_err_t ret = ESP_OK ;
if ( i2c_master - > status = = I2C_STATUS_TIMEOUT | | i2c_ll_is_bus_busy ( hal - > dev ) ) {
s_i2c_hw_fsm_reset ( i2c_master ) ;
}
if ( i2c_master - > base - > pm_lock ) {
ESP_RETURN_ON_ERROR ( esp_pm_lock_acquire ( i2c_master - > base - > pm_lock ) , TAG , " acquire pm_lock failed " ) ;
}
portENTER_CRITICAL ( & i2c_master - > base - > spinlock ) ;
atomic_init ( & i2c_master - > trans_idx , 0 ) ;
atomic_store ( & i2c_master - > status , I2C_STATUS_IDLE ) ;
i2c_master - > cmd_idx = 0 ;
i2c_master - > rx_cnt = 0 ;
i2c_master - > read_len_static = 0 ;
i2c_hal_set_bus_timing ( hal , i2c_dev - > scl_speed_hz , i2c_master - > base - > clk_src , i2c_master - > base - > clk_src_freq_hz ) ;
i2c_ll_master_set_fractional_divider ( hal - > dev , 0 , 0 ) ;
i2c_ll_update ( hal - > dev ) ;
i2c_ll_txfifo_rst ( hal - > dev ) ;
i2c_ll_rxfifo_rst ( hal - > dev ) ;
i2c_ll_enable_intr_mask ( hal - > dev , I2C_LL_MASTER_EVENT_INTR ) ;
portEXIT_CRITICAL ( & i2c_master - > base - > spinlock ) ;
if ( i2c_master - > asnyc_trans = = true ) {
s_i2c_send_command_async ( i2c_master , NULL ) ;
} else {
s_i2c_send_commands ( i2c_master , ticks_to_wait ) ;
// Wait event bits
if ( i2c_master - > status ! = I2C_STATUS_DONE ) {
ret = ESP_ERR_INVALID_STATE ;
}
}
if ( i2c_master - > base - > pm_lock ) {
ESP_RETURN_ON_ERROR ( esp_pm_lock_release ( i2c_master - > base - > pm_lock ) , TAG , " release pm_lock failed " ) ;
}
return ret ;
}
///////////////////////////////I2C DRIVERS//////////////////////////////////////////////////////////////
IRAM_ATTR static void i2c_isr_receive_handler ( i2c_master_bus_t * i2c_master )
{
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
while ( i2c_ll_is_bus_busy ( hal - > dev ) ) { }
if ( i2c_master - > status = = I2C_STATUS_READ ) {
i2c_operation_t * i2c_operation = & i2c_master - > i2c_trans . ops [ i2c_master - > trans_idx ] ;
portENTER_CRITICAL_ISR ( & i2c_master - > base - > spinlock ) ;
i2c_ll_read_rxfifo ( hal - > dev , i2c_operation - > data + i2c_operation - > bytes_used , i2c_master - > rx_cnt ) ;
/* rx_cnt bytes have just been read, increment the number of bytes used from the buffer */
i2c_master - > w_r_size = i2c_master - > rx_cnt ;
i2c_operation - > bytes_used + = i2c_master - > rx_cnt ;
i2c_master - > cmd_idx = 0 ;
/* Test if there are still some remaining bytes to send. */
if ( i2c_operation - > bytes_used = = i2c_operation - > total_bytes ) {
i2c_master - > i2c_trans . cmd_count - - ;
i2c_master - > read_buf_pos = i2c_master - > trans_idx ;
i2c_master - > trans_idx + + ;
i2c_operation - > bytes_used = 0 ;
}
portEXIT_CRITICAL_ISR ( & i2c_master - > base - > spinlock ) ;
}
# if !SOC_I2C_STOP_INDEPENDENT
else {
i2c_operation_t * i2c_operation = & i2c_master - > i2c_trans . ops [ i2c_master - > read_buf_pos ] ;
portENTER_CRITICAL_ISR ( & i2c_master - > base - > spinlock ) ;
i2c_ll_read_rxfifo ( hal - > dev , i2c_operation - > data + i2c_operation - > bytes_used , i2c_master - > read_len_static ) ;
i2c_ll_read_rxfifo ( hal - > dev , i2c_master - > i2c_trans . ops [ i2c_master - > read_buf_pos + 1 ] . data , 1 ) ;
i2c_master - > w_r_size = i2c_master - > read_len_static + 1 ;
i2c_master - > contains_read = false ;
portEXIT_CRITICAL_ISR ( & i2c_master - > base - > spinlock ) ;
}
# endif
}
static void IRAM_ATTR i2c_master_isr_handler_default ( void * arg )
{
i2c_master_bus_handle_t i2c_master = ( i2c_master_bus_t * ) arg ;
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
portBASE_TYPE HPTaskAwoken = pdFALSE ;
uint32_t int_mask ;
BaseType_t ret = pdTRUE ;
i2c_master - > trans_done = false ;
i2c_ll_get_intr_mask ( hal - > dev , & int_mask ) ;
i2c_ll_clear_intr_mask ( hal - > dev , int_mask ) ;
if ( int_mask = = 0 ) {
return ;
}
if ( int_mask = = I2C_LL_INTR_NACK ) {
atomic_store ( & i2c_master - > status , I2C_STATUS_ACK_ERROR ) ;
i2c_master - > event = I2C_EVENT_NACK ;
} else if ( int_mask = = I2C_LL_INTR_TIMEOUT | | int_mask = = I2C_LL_INTR_ARBITRATION ) {
atomic_store ( & i2c_master - > status , I2C_STATUS_TIMEOUT ) ;
} else if ( int_mask = = I2C_LL_INTR_MST_COMPLETE ) {
i2c_master - > trans_done = true ;
i2c_master - > event = I2C_EVENT_DONE ;
}
if ( i2c_master - > contains_read = = true ) {
i2c_isr_receive_handler ( i2c_master ) ;
}
if ( i2c_master - > asnyc_trans ) {
i2c_master_dev_handle_t i2c_dev = NULL ;
i2c_master_device_list_t * device_item ;
xSemaphoreTakeFromISR ( i2c_master - > bus_lock_mux , & HPTaskAwoken ) ;
SLIST_FOREACH ( device_item , & i2c_master - > device_list , next ) {
if ( device_item - > device - > device_address = = i2c_master - > i2c_trans . device_address ) {
i2c_dev = device_item - > device ;
break ;
}
}
xSemaphoreGiveFromISR ( i2c_master - > bus_lock_mux , & HPTaskAwoken ) ;
if ( i2c_dev = = NULL ) {
return ;
}
i2c_master_event_data_t evt = {
. event = i2c_master - > event ,
} ;
s_i2c_send_command_async ( i2c_master , & HPTaskAwoken ) ;
if ( i2c_master - > trans_done ) {
if ( i2c_dev - > on_trans_done ) {
i2c_dev - > on_trans_done ( i2c_dev , & evt , i2c_dev - > user_ctx ) ;
}
if ( i2c_master - > new_queue & & i2c_master - > num_trans_inqueue > 0 & & i2c_master - > in_progress = = false ) {
i2c_transaction_t t = { } ;
i2c_master - > num_trans_inqueue - - ;
i2c_master - > new_queue = false ;
t . cmd_count = 0 ;
t . device_address = 0 ;
t . ops = NULL ;
ret = xQueueReceiveFromISR ( i2c_master - > trans_queues [ I2C_TRANS_QUEUE_PROGRESS ] , & t , & HPTaskAwoken ) ;
if ( ret = = pdTRUE ) {
i2c_master - > queue_trans = true ;
atomic_init ( & i2c_master - > trans_idx , 0 ) ;
atomic_store ( & i2c_master - > status , I2C_STATUS_IDLE ) ;
i2c_master - > cmd_idx = 0 ;
i2c_master - > rx_cnt = 0 ;
i2c_master - > read_len_static = 0 ;
i2c_ll_txfifo_rst ( hal - > dev ) ;
i2c_ll_rxfifo_rst ( hal - > dev ) ;
i2c_ll_enable_intr_mask ( hal - > dev , I2C_LL_MASTER_EVENT_INTR ) ;
i2c_master - > i2c_trans = t ;
memcpy ( i2c_master - > i2c_ops , t . ops , t . cmd_count * sizeof ( i2c_operation_t ) ) ;
s_i2c_send_command_async ( i2c_master , & HPTaskAwoken ) ;
}
}
}
} else {
xSemaphoreGiveFromISR ( i2c_master - > cmd_semphr , & HPTaskAwoken ) ;
}
if ( HPTaskAwoken = = pdTRUE ) {
portYIELD_FROM_ISR ( ) ;
}
}
static esp_err_t i2c_param_master_config ( i2c_bus_handle_t handle , const i2c_master_bus_config_t * i2c_conf )
{
i2c_hal_context_t * hal = & handle - > hal ;
ESP_RETURN_ON_ERROR ( i2c_common_set_pins ( handle ) , TAG , " i2c master set pin failed " ) ;
ESP_RETURN_ON_ERROR ( i2c_select_periph_clock ( handle , i2c_conf - > clk_source ) , TAG , " i2c select clock failed " ) ;
handle - > clk_src = i2c_conf - > clk_source ;
portENTER_CRITICAL ( & handle - > spinlock ) ;
i2c_hal_master_init ( hal ) ;
i2c_ll_update ( hal - > dev ) ;
portEXIT_CRITICAL ( & handle - > spinlock ) ;
return ESP_OK ;
}
static esp_err_t i2c_master_bus_destroy ( i2c_master_bus_handle_t bus_handle )
{
ESP_RETURN_ON_FALSE ( bus_handle , ESP_ERR_INVALID_ARG , TAG , " no memory for i2c master bus " ) ;
i2c_master_bus_handle_t i2c_master = bus_handle ;
2023-08-11 00:14:09 -04:00
if ( i2c_release_bus_handle ( i2c_master - > base ) = = ESP_OK ) {
2023-08-03 00:29:44 -04:00
if ( i2c_master ) {
if ( i2c_master - > bus_lock_mux ) {
vSemaphoreDeleteWithCaps ( i2c_master - > bus_lock_mux ) ;
i2c_master - > bus_lock_mux = NULL ;
}
if ( i2c_master - > cmd_semphr ) {
vSemaphoreDeleteWithCaps ( i2c_master - > cmd_semphr ) ;
i2c_master - > cmd_semphr = NULL ;
}
if ( i2c_master - > queues_storage ) {
free ( i2c_master - > queues_storage ) ;
}
if ( i2c_master - > i2c_anyc_ops ) {
for ( int i = 0 ; i < i2c_master - > index ; i + + ) {
if ( i2c_master - > i2c_anyc_ops [ i ] ) {
free ( i2c_master - > i2c_anyc_ops [ i ] ) ;
}
}
free ( i2c_master - > i2c_anyc_ops ) ;
}
if ( i2c_master - > anyc_write_buffer ) {
for ( int i = 0 ; i < i2c_master - > index ; i + + ) {
if ( i2c_master - > anyc_write_buffer [ i ] ) {
free ( i2c_master - > anyc_write_buffer [ i ] ) ;
}
}
free ( i2c_master - > anyc_write_buffer ) ;
}
for ( int i = 0 ; i < I2C_TRANS_QUEUE_MAX ; i + + ) {
if ( i2c_master - > trans_queues [ i ] ) {
vQueueDelete ( i2c_master - > trans_queues [ i ] ) ;
}
}
bus_handle = NULL ;
}
free ( i2c_master ) ;
} else {
free ( i2c_master ) ;
}
return ESP_OK ;
}
static esp_err_t s_i2c_asynchronous_transaction ( i2c_master_dev_handle_t i2c_dev , i2c_operation_t * i2c_ops , size_t ops_dim , int timeout_ms )
{
if ( i2c_dev - > master_bus - > sent_all = = true & & i2c_dev - > master_bus - > num_trans_inqueue = = 0 ) {
memcpy ( i2c_dev - > master_bus - > i2c_ops , i2c_ops , sizeof ( i2c_operation_t ) * ops_dim ) ;
i2c_dev - > master_bus - > addr_10bits_bus = i2c_dev - > addr_10bits ;
i2c_dev - > master_bus - > i2c_trans = ( i2c_transaction_t ) {
. device_address = i2c_dev - > device_address ,
. ops = i2c_dev - > master_bus - > i2c_ops ,
. cmd_count = ops_dim ,
} ;
i2c_dev - > master_bus - > sent_all = false ;
i2c_dev - > master_bus - > trans_finish = false ;
i2c_dev - > master_bus - > queue_trans = false ;
ESP_RETURN_ON_ERROR ( s_i2c_transaction_start ( i2c_dev , timeout_ms ) , TAG , " I2C transaction failed " ) ;
} else {
i2c_dev - > master_bus - > i2c_anyc_ops [ i2c_dev - > master_bus - > index ] = ( i2c_operation_t ( * ) ) heap_caps_calloc ( 1 , sizeof ( i2c_operation_t ) * 6 , I2C_MEM_ALLOC_CAPS ) ;
memcpy ( i2c_dev - > master_bus - > i2c_anyc_ops [ i2c_dev - > master_bus - > index ] , i2c_ops , sizeof ( i2c_operation_t ) * ops_dim ) ;
i2c_transaction_t i2c_queue_pre ;
if ( i2c_dev - > master_bus - > num_trans_inflight < i2c_dev - > master_bus - > queue_size ) {
ESP_RETURN_ON_FALSE ( xQueueReceive ( i2c_dev - > master_bus - > trans_queues [ I2C_TRANS_QUEUE_READY ] , & i2c_queue_pre , portMAX_DELAY ) = = pdTRUE , ESP_FAIL , TAG , " no transaction in the ready queue " ) ;
} else {
ESP_RETURN_ON_FALSE ( xQueueReceive ( i2c_dev - > master_bus - > trans_queues [ I2C_TRANS_QUEUE_COMPLETE ] , & i2c_queue_pre , portMAX_DELAY ) = = pdTRUE , ESP_FAIL , TAG , " recycle transaction from done queue failed " ) ;
i2c_dev - > master_bus - > num_trans_inflight - - ;
}
i2c_queue_pre = ( i2c_transaction_t ) {
. device_address = i2c_dev - > device_address ,
. ops = i2c_dev - > master_bus - > i2c_anyc_ops [ i2c_dev - > master_bus - > index ] ,
. cmd_count = ops_dim ,
} ;
i2c_dev - > master_bus - > index + + ;
if ( xQueueSend ( i2c_dev - > master_bus - > trans_queues [ I2C_TRANS_QUEUE_PROGRESS ] , & i2c_queue_pre , portMAX_DELAY ) = = pdTRUE ) {
i2c_dev - > master_bus - > num_trans_inflight + + ;
i2c_dev - > master_bus - > num_trans_inqueue + + ;
if ( i2c_dev - > master_bus - > sent_all = = true ) {
// Oh no, you cannot get the queue from ISR, so you get queue here.
2023-08-11 00:14:09 -04:00
ESP_RETURN_ON_FALSE ( xQueueReceive ( i2c_dev - > master_bus - > trans_queues [ I2C_TRANS_QUEUE_PROGRESS ] , & i2c_queue_pre , portMAX_DELAY ) = = pdTRUE , ESP_FAIL , TAG , " get trans from progress queue failed " ) ;
2023-08-03 00:29:44 -04:00
i2c_dev - > master_bus - > num_trans_inflight - - ;
i2c_dev - > master_bus - > num_trans_inqueue - - ;
i2c_dev - > master_bus - > sent_all = false ;
i2c_dev - > master_bus - > trans_finish = false ;
i2c_dev - > master_bus - > queue_trans = false ;
ESP_RETURN_ON_ERROR ( s_i2c_transaction_start ( i2c_dev , timeout_ms ) , TAG , " I2C transaction failed " ) ;
}
} else {
ESP_RETURN_ON_FALSE ( xQueueSend ( i2c_dev - > master_bus - > trans_queues [ I2C_TRANS_QUEUE_READY ] , & i2c_queue_pre , 0 ) = = pdTRUE , ESP_ERR_INVALID_STATE , TAG , " ready queue full " ) ;
}
}
return ESP_OK ;
}
static esp_err_t s_i2c_synchronous_transaction ( i2c_master_dev_handle_t i2c_dev , i2c_operation_t * i2c_ops , size_t ops_dim , int timeout_ms )
{
i2c_dev - > master_bus - > trans_done = false ;
TickType_t ticks_to_wait = ( timeout_ms = = - 1 ) ? portMAX_DELAY : pdMS_TO_TICKS ( timeout_ms ) ;
if ( xSemaphoreTake ( i2c_dev - > master_bus - > bus_lock_mux , ticks_to_wait ) ! = pdTRUE ) {
return ESP_ERR_TIMEOUT ;
}
memcpy ( i2c_dev - > master_bus - > i2c_ops , i2c_ops , sizeof ( i2c_operation_t ) * ops_dim ) ;
i2c_dev - > master_bus - > addr_10bits_bus = i2c_dev - > addr_10bits ;
i2c_dev - > master_bus - > i2c_trans = ( i2c_transaction_t ) {
. device_address = i2c_dev - > device_address ,
. ops = i2c_dev - > master_bus - > i2c_ops ,
. cmd_count = ops_dim ,
} ;
i2c_dev - > master_bus - > sent_all = false ;
i2c_dev - > master_bus - > trans_finish = false ;
i2c_dev - > master_bus - > queue_trans = false ;
ESP_RETURN_ON_ERROR ( s_i2c_transaction_start ( i2c_dev , timeout_ms ) , TAG , " I2C transaction failed " ) ;
xSemaphoreGive ( i2c_dev - > master_bus - > bus_lock_mux ) ;
return ESP_OK ;
}
esp_err_t i2c_new_master_bus ( const i2c_master_bus_config_t * bus_config , i2c_master_bus_handle_t * ret_bus_handle )
{
# if CONFIG_I2C_ENABLE_DEBUG_LOG
esp_log_level_set ( TAG , ESP_LOG_DEBUG ) ;
# endif
esp_err_t ret = ESP_OK ;
i2c_master_bus_t * i2c_master = NULL ;
i2c_port_num_t i2c_port_num = bus_config - > i2c_port ;
ESP_RETURN_ON_FALSE ( bus_config , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
ESP_RETURN_ON_FALSE ( ( bus_config - > i2c_port < SOC_I2C_NUM | | bus_config - > i2c_port = = - 1 ) , ESP_ERR_INVALID_ARG , TAG , " invalid i2c port number " ) ;
ESP_RETURN_ON_FALSE ( GPIO_IS_VALID_GPIO ( bus_config - > sda_io_num ) & & GPIO_IS_VALID_GPIO ( bus_config - > scl_io_num ) , ESP_ERR_INVALID_ARG , TAG , " invalid SDA/SCL pin number " ) ;
i2c_master = heap_caps_calloc ( 1 , sizeof ( i2c_master_bus_t ) + 20 * sizeof ( i2c_transaction_t ) , I2C_MEM_ALLOC_CAPS ) ;
ESP_RETURN_ON_FALSE ( i2c_master , ESP_ERR_NO_MEM , TAG , " no memory for i2c master bus " ) ;
2023-10-24 06:44:49 -04:00
ESP_GOTO_ON_ERROR ( i2c_acquire_bus_handle ( i2c_port_num , & i2c_master - > base , I2C_BUS_MODE_MASTER ) , err , TAG , " I2C bus acquire failed " ) ;
i2c_port_num = i2c_master - > base - > port_num ;
2023-08-03 00:29:44 -04:00
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
i2c_master - > base - > scl_num = bus_config - > scl_io_num ;
i2c_master - > base - > sda_num = bus_config - > sda_io_num ;
i2c_master - > base - > pull_up_enable = bus_config - > flags . enable_internal_pullup ;
ESP_GOTO_ON_ERROR ( i2c_param_master_config ( i2c_master - > base , bus_config ) , err , TAG , " i2c configure parameter failed " ) ;
i2c_master - > bus_lock_mux = xSemaphoreCreateBinaryWithCaps ( I2C_MEM_ALLOC_CAPS ) ;
ESP_GOTO_ON_FALSE ( i2c_master - > bus_lock_mux , ESP_ERR_NO_MEM , err , TAG , " No memory for binary semaphore " ) ;
xSemaphoreGive ( i2c_master - > bus_lock_mux ) ;
i2c_master - > cmd_semphr = xSemaphoreCreateBinaryWithCaps ( I2C_MEM_ALLOC_CAPS ) ;
ESP_GOTO_ON_FALSE ( i2c_master - > cmd_semphr , ESP_ERR_NO_MEM , err , TAG , " no memory for i2c semaphore struct " ) ;
portENTER_CRITICAL ( & i2c_master - > base - > spinlock ) ;
i2c_ll_clear_intr_mask ( hal - > dev , I2C_LL_MASTER_EVENT_INTR ) ;
portEXIT_CRITICAL ( & i2c_master - > base - > spinlock ) ;
if ( bus_config - > intr_priority ) {
ESP_RETURN_ON_FALSE ( 1 < < ( bus_config - > intr_priority ) & I2C_ALLOW_INTR_PRIORITY_MASK , ESP_ERR_INVALID_ARG , TAG , " invalid interrupt priority:%d " , bus_config - > intr_priority ) ;
}
xSemaphoreTake ( i2c_master - > bus_lock_mux , portMAX_DELAY ) ;
SLIST_INIT ( & i2c_master - > device_list ) ;
xSemaphoreGive ( i2c_master - > bus_lock_mux ) ;
// Initialize the queue
if ( bus_config - > trans_queue_depth ) {
i2c_master - > asnyc_trans = true ;
i2c_master - > sent_all = true ;
i2c_master - > trans_finish = true ;
i2c_master - > new_queue = true ;
i2c_master - > queue_size = bus_config - > trans_queue_depth ;
i2c_master - > queues_storage = ( uint8_t * ) heap_caps_calloc ( bus_config - > trans_queue_depth * I2C_TRANS_QUEUE_MAX , sizeof ( i2c_transaction_t ) , I2C_MEM_ALLOC_CAPS ) ;
ESP_RETURN_ON_FALSE ( i2c_master - > queues_storage , ESP_ERR_NO_MEM , TAG , " no mem for queue storage " ) ;
i2c_transaction_t * * pp_trans_desc = ( i2c_transaction_t * * ) i2c_master - > queues_storage ;
for ( int i = 0 ; i < I2C_TRANS_QUEUE_MAX ; i + + ) {
i2c_master - > trans_queues [ i ] = xQueueCreate ( bus_config - > trans_queue_depth , sizeof ( i2c_transaction_t ) ) ;
pp_trans_desc + = bus_config - > trans_queue_depth ;
// sanity check
assert ( i2c_master - > trans_queues [ i ] ) ;
}
i2c_transaction_t trans_pre = { } ;
for ( int i = 0 ; i < bus_config - > trans_queue_depth ; i + + ) {
trans_pre = i2c_master - > i2c_trans_pool [ i ] ;
ESP_RETURN_ON_FALSE ( xQueueSend ( i2c_master - > trans_queues [ I2C_TRANS_QUEUE_READY ] , & trans_pre , 0 ) = = pdTRUE ,
ESP_ERR_INVALID_STATE , TAG , " ready queue full " ) ;
}
i2c_master - > i2c_anyc_ops = ( i2c_operation_t ( * * ) ) heap_caps_calloc ( bus_config - > trans_queue_depth , sizeof ( i2c_operation_t * ) , I2C_MEM_ALLOC_CAPS ) ;
i2c_master - > anyc_write_buffer = ( uint8_t * * ) heap_caps_calloc ( bus_config - > trans_queue_depth , sizeof ( uint8_t * ) , I2C_MEM_ALLOC_CAPS ) ;
}
int isr_flags = I2C_INTR_ALLOC_FLAG ;
if ( bus_config - > intr_priority ) {
isr_flags | = 1 < < ( bus_config - > intr_priority ) ;
}
ret = esp_intr_alloc_intrstatus ( i2c_periph_signal [ i2c_port_num ] . irq , isr_flags , ( uint32_t ) i2c_ll_get_interrupt_status_reg ( hal - > dev ) , I2C_LL_MASTER_EVENT_INTR , i2c_master_isr_handler_default , i2c_master , & i2c_master - > base - > intr_handle ) ;
ESP_GOTO_ON_ERROR ( ret , err , TAG , " install i2c master interrupt failed " ) ;
atomic_init ( & i2c_master - > status , I2C_STATUS_IDLE ) ;
i2c_ll_enable_intr_mask ( hal - > dev , I2C_LL_MASTER_EVENT_INTR ) ;
i2c_ll_master_set_filter ( hal - > dev , bus_config - > glitch_ignore_cnt ) ;
xSemaphoreGive ( i2c_master - > cmd_semphr ) ;
* ret_bus_handle = i2c_master ;
return ESP_OK ;
err :
if ( i2c_master ) {
i2c_master_bus_destroy ( i2c_master ) ;
}
return ret ;
}
esp_err_t i2c_master_bus_add_device ( i2c_master_bus_handle_t bus_handle , const i2c_device_config_t * dev_config , i2c_master_dev_handle_t * ret_handle )
{
esp_err_t ret = ESP_OK ;
ESP_RETURN_ON_FALSE ( ( bus_handle ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " this bus is not initialized, please call `i2c_new_master_bus` " ) ;
if ( bus_handle - > base - > bus_mode ! = I2C_BUS_MODE_MASTER ) {
ESP_LOGE ( TAG , " This is not master bus! " ) ;
return ESP_ERR_INVALID_ARG ;
}
i2c_master_bus_t * i2c_master = bus_handle ;
i2c_master_dev_t * i2c_dev = heap_caps_calloc ( 1 , sizeof ( i2c_master_dev_t ) , I2C_MEM_ALLOC_CAPS ) ;
ESP_GOTO_ON_FALSE ( i2c_dev , ESP_ERR_NO_MEM , err , TAG , " no memory for i2c master device " ) ;
i2c_dev - > device_address = dev_config - > device_address ;
i2c_dev - > scl_speed_hz = dev_config - > scl_speed_hz ;
i2c_dev - > addr_10bits = dev_config - > dev_addr_length ;
i2c_dev - > master_bus = i2c_master ;
i2c_master_device_list_t * device_item = ( i2c_master_device_list_t * ) calloc ( 1 , sizeof ( i2c_master_device_list_t ) ) ;
2023-08-11 00:14:09 -04:00
ESP_GOTO_ON_FALSE ( ( device_item ! = NULL ) , ESP_ERR_NO_MEM , err , TAG , " no memory for i2c device item` " ) ;
2023-08-03 00:29:44 -04:00
device_item - > device = i2c_dev ;
xSemaphoreTake ( bus_handle - > bus_lock_mux , portMAX_DELAY ) ;
SLIST_INSERT_HEAD ( & bus_handle - > device_list , device_item , next ) ;
xSemaphoreGive ( bus_handle - > bus_lock_mux ) ;
* ret_handle = i2c_dev ;
return ret ;
err :
if ( i2c_dev ) {
i2c_master_bus_rm_device ( i2c_dev ) ;
}
return ret ;
}
esp_err_t i2c_master_bus_rm_device ( i2c_master_dev_handle_t handle )
{
ESP_RETURN_ON_FALSE ( ( handle ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " this device is not initialized " ) ;
ESP_RETURN_ON_FALSE ( ( ( ( int ) atomic_load ( & handle - > master_bus - > status ) > ( int ) I2C_STATUS_START ) ) , ESP_ERR_INVALID_STATE , TAG , " Wrong I2C status, cannot delete device " ) ;
i2c_master_bus_handle_t i2c_master = handle - > master_bus ;
i2c_master_device_list_t * device_item ;
xSemaphoreTake ( handle - > master_bus - > bus_lock_mux , portMAX_DELAY ) ;
SLIST_FOREACH ( device_item , & i2c_master - > device_list , next ) {
if ( handle = = device_item - > device ) {
SLIST_REMOVE ( & i2c_master - > device_list , device_item , i2c_master_device_list , next ) ;
free ( device_item ) ;
break ;
}
}
xSemaphoreGive ( handle - > master_bus - > bus_lock_mux ) ;
if ( handle ) {
free ( handle ) ;
handle = NULL ;
}
return ESP_OK ;
}
esp_err_t i2c_del_master_bus ( i2c_master_bus_handle_t bus_handle )
{
ESP_LOGD ( TAG , " del i2c bus(%d) " , bus_handle - > base - > port_num ) ;
ESP_RETURN_ON_ERROR ( i2c_master_bus_destroy ( bus_handle ) , TAG , " destroy i2c bus failed " ) ;
return ESP_OK ;
}
esp_err_t i2c_master_bus_reset ( i2c_master_bus_handle_t handle )
{
ESP_RETURN_ON_FALSE ( ( handle ! = NULL ) , ESP_ERR_INVALID_ARG , TAG , " This bus is not initialized " ) ;
// Reset I2C master bus
ESP_RETURN_ON_ERROR ( s_i2c_hw_fsm_reset ( handle ) , TAG , " I2C master bus reset failed " ) ;
// Reset I2C status state
atomic_store ( & handle - > status , I2C_STATUS_IDLE ) ;
return ESP_OK ;
}
esp_err_t i2c_master_transmit ( i2c_master_dev_handle_t i2c_dev , const uint8_t * write_buffer , size_t write_size , int xfer_timeout_ms )
{
ESP_RETURN_ON_FALSE ( i2c_dev ! = NULL , ESP_ERR_INVALID_ARG , TAG , " i2c handle not initialized " ) ;
ESP_RETURN_ON_FALSE ( ( write_buffer ! = NULL ) & & ( write_size > 0 ) , ESP_ERR_INVALID_ARG , TAG , " i2c transmit buffer or size invalid " ) ;
if ( i2c_dev - > master_bus - > asnyc_trans = = false ) {
i2c_operation_t i2c_ops [ ] = {
{ . hw_cmd = I2C_TRANS_START_COMMAND } ,
{ . hw_cmd = I2C_TRANS_WRITE_COMMAND ( false ) , . data = ( uint8_t * ) write_buffer , . total_bytes = write_size } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
ESP_RETURN_ON_ERROR ( s_i2c_synchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
} else {
i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] = ( uint8_t * ) heap_caps_calloc ( 1 , sizeof ( uint8_t ) * write_size , I2C_MEM_ALLOC_CAPS ) ;
memcpy ( i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] , write_buffer , write_size ) ;
i2c_operation_t i2c_ops [ ] = {
{ . hw_cmd = I2C_TRANS_START_COMMAND } ,
{ . hw_cmd = I2C_TRANS_WRITE_COMMAND ( false ) , . data = ( uint8_t * ) i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] , . total_bytes = write_size } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
ESP_RETURN_ON_ERROR ( s_i2c_asynchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
}
return ESP_OK ;
}
esp_err_t i2c_master_transmit_receive ( i2c_master_dev_handle_t i2c_dev , const uint8_t * write_buffer , size_t write_size , uint8_t * read_buffer , size_t read_size , int xfer_timeout_ms )
{
ESP_RETURN_ON_FALSE ( i2c_dev ! = NULL , ESP_ERR_INVALID_ARG , TAG , " i2c handle not initialized " ) ;
ESP_RETURN_ON_FALSE ( ( write_buffer ! = NULL ) & & ( write_size > 0 ) , ESP_ERR_INVALID_ARG , TAG , " i2c transmit buffer or size invalid " ) ;
ESP_RETURN_ON_FALSE ( ( read_buffer ! = NULL ) & & ( read_size > 0 ) , ESP_ERR_INVALID_ARG , TAG , " i2c receive buffer or size invalid " ) ;
if ( i2c_dev - > master_bus - > asnyc_trans = = false ) {
i2c_operation_t i2c_ops [ ] = {
{ . hw_cmd = I2C_TRANS_START_COMMAND } ,
{ . hw_cmd = I2C_TRANS_WRITE_COMMAND ( false ) , . data = ( uint8_t * ) write_buffer , . total_bytes = write_size } ,
{ . 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 ( NACK_VAL ) , . data = ( read_buffer + read_size - 1 ) , . total_bytes = 1 } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
ESP_RETURN_ON_ERROR ( s_i2c_synchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
} else {
i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] = ( uint8_t * ) heap_caps_calloc ( 1 , sizeof ( uint8_t ) * write_size , I2C_MEM_ALLOC_CAPS ) ;
memcpy ( i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] , write_buffer , write_size ) ;
i2c_operation_t i2c_ops [ ] = {
{ . hw_cmd = I2C_TRANS_START_COMMAND } ,
{ . hw_cmd = I2C_TRANS_WRITE_COMMAND ( false ) , . data = ( uint8_t * ) i2c_dev - > master_bus - > anyc_write_buffer [ i2c_dev - > master_bus - > index ] , . total_bytes = write_size } ,
{ . 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 ( NACK_VAL ) , . data = ( read_buffer + read_size - 1 ) , . total_bytes = 1 } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
ESP_RETURN_ON_ERROR ( s_i2c_asynchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
}
return ESP_OK ;
}
esp_err_t i2c_master_receive ( i2c_master_dev_handle_t i2c_dev , uint8_t * read_buffer , size_t read_size , int xfer_timeout_ms )
{
ESP_RETURN_ON_FALSE ( i2c_dev ! = NULL , ESP_ERR_INVALID_ARG , TAG , " i2c handle not initialized " ) ;
ESP_RETURN_ON_FALSE ( ( read_buffer ! = NULL ) & & ( read_size > 0 ) , ESP_ERR_INVALID_ARG , TAG , " i2c receive buffer or size invalid " ) ;
i2c_operation_t i2c_ops [ ] = {
{ . 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 ( NACK_VAL ) , . data = ( read_buffer + read_size - 1 ) , . total_bytes = 1 } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
if ( i2c_dev - > master_bus - > asnyc_trans = = false ) {
ESP_RETURN_ON_ERROR ( s_i2c_synchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
} else {
ESP_RETURN_ON_ERROR ( s_i2c_asynchronous_transaction ( i2c_dev , i2c_ops , DIM ( i2c_ops ) , xfer_timeout_ms ) , TAG , " I2C transaction failed " ) ;
}
return ESP_OK ;
}
esp_err_t i2c_master_probe ( i2c_master_bus_handle_t i2c_master , uint16_t address , int xfer_timeout_ms )
{
ESP_RETURN_ON_FALSE ( i2c_master ! = NULL , ESP_ERR_INVALID_ARG , TAG , " i2c handle not initialized " ) ;
2023-09-05 00:27:18 -04:00
TickType_t ticks_to_wait = ( xfer_timeout_ms = = - 1 ) ? portMAX_DELAY : pdMS_TO_TICKS ( xfer_timeout_ms ) ;
if ( xSemaphoreTake ( i2c_master - > bus_lock_mux , ticks_to_wait ) ! = pdTRUE ) {
return ESP_ERR_TIMEOUT ;
}
i2c_master - > cmd_idx = 0 ;
i2c_master - > trans_idx = 0 ;
i2c_master - > trans_done = false ;
2023-08-31 03:06:46 -04:00
i2c_hal_context_t * hal = & i2c_master - > base - > hal ;
2023-08-03 00:29:44 -04:00
i2c_operation_t i2c_ops [ ] = {
{ . hw_cmd = I2C_TRANS_START_COMMAND } ,
{ . hw_cmd = I2C_TRANS_STOP_COMMAND } ,
} ;
i2c_master - > i2c_trans = ( i2c_transaction_t ) {
. device_address = address ,
. ops = i2c_ops ,
. cmd_count = DIM ( i2c_ops ) ,
} ;
2023-08-31 03:06:46 -04:00
// I2C probe does not have i2c device module. So set the clock parameter independently
// This will not influence device transaction.
i2c_hal_set_bus_timing ( hal , 100000 , i2c_master - > base - > clk_src , i2c_master - > base - > clk_src_freq_hz ) ;
i2c_ll_master_set_fractional_divider ( hal - > dev , 0 , 0 ) ;
i2c_ll_update ( hal - > dev ) ;
2023-08-03 00:29:44 -04:00
s_i2c_send_commands ( i2c_master , ticks_to_wait ) ;
if ( i2c_master - > status = = I2C_STATUS_ACK_ERROR ) {
2023-08-31 03:06:46 -04:00
// Reset the status to done, in order not influence next time transaction.
i2c_master - > status = I2C_STATUS_DONE ;
2023-09-05 00:27:18 -04:00
xSemaphoreGive ( i2c_master - > bus_lock_mux ) ;
2023-08-03 00:29:44 -04:00
return ESP_ERR_NOT_FOUND ;
}
2023-09-05 00:27:18 -04:00
xSemaphoreGive ( i2c_master - > bus_lock_mux ) ;
2023-08-03 00:29:44 -04:00
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_RETURN_ON_FALSE ( i2c_dev ! = NULL , ESP_ERR_INVALID_ARG , TAG , " i2c handle not initialized " ) ;
if ( i2c_dev - > master_bus - > asnyc_trans = = false ) {
ESP_LOGE ( TAG , " I2C transaction queue is not initialized, so you can't use callback here, please resister the bus again with trans_queue_depth != 0 " ) ;
return ESP_ERR_INVALID_STATE ;
}
# if CONFIG_I2C_ISR_IRAM_SAFE
if ( cbs - > on_trans_done ) {
ESP_RETURN_ON_FALSE ( esp_ptr_in_iram ( cbs - > on_trans_done ) , ESP_ERR_INVALID_ARG , TAG , " i2c trans done callback not in IRAM " ) ;
}
if ( user_data ) {
ESP_RETURN_ON_FALSE ( esp_ptr_internal ( user_data ) , ESP_ERR_INVALID_ARG , TAG , " user context not in internal RAM " ) ;
}
# endif // CONFIG_I2C_ISR_IRAM_SAFE
i2c_dev - > on_trans_done = cbs - > on_trans_done ;
i2c_dev - > user_ctx = user_data ;
return ESP_OK ;
}
esp_err_t i2c_master_wait_all_done ( i2c_master_bus_handle_t i2c_master , int timeout_ms )
{
ESP_RETURN_ON_FALSE ( i2c_master , ESP_ERR_INVALID_ARG , TAG , " invalid argument " ) ;
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS ( timeout_ms ) ;
i2c_transaction_t t ;
size_t cnt = i2c_master - > num_trans_inflight ;
for ( size_t i = 0 ; i < cnt ; i + + ) {
ESP_RETURN_ON_FALSE ( xQueueReceive ( i2c_master - > trans_queues [ I2C_TRANS_QUEUE_COMPLETE ] , & t , wait_ticks ) = = pdTRUE ,
ESP_ERR_TIMEOUT , TAG , " flush timeout " ) ;
ESP_RETURN_ON_FALSE ( xQueueSend ( i2c_master - > trans_queues [ I2C_TRANS_QUEUE_READY ] , & t , 0 ) = = pdTRUE ,
ESP_ERR_INVALID_STATE , TAG , " ready queue full " ) ;
i2c_master - > num_trans_inflight - - ;
}
return ESP_OK ;
}