bugfix(i2c): add I2C hardware reset if the hw FSM get stuck
Reported from different sources from github or bbs: https://github.com/espressif/esp-idf/issues/680 https://github.com/espressif/esp-idf/issues/922 We tested reading several sensor or other I2C slave devices, if the power and SDA/SCL wires are in proper condition, everything works find with reading the slave. If we remove the power supply for the slave during I2C is reading, or directly connect SDA or SCL to ground, this would cause the I2C FSM get stuck in wrong state, all we can do is the reset the I2C hardware in this case. After this commit, no matter whether the power supply of I2C slave is removed or SDA / SCL are shorted to ground, the driver can recover from wrong state. We are not sure whether this the save issue with the reported one yet, but to make the driver more robust. Further information: 1. For I2C master mode, we have tested different situations, e.g., to short the SDA/SCL directly to GND/VCC, to short the SDA to SCL, to un-plug the slave device, to power off the slave device. Under all of those situations, this version of driver can recover and keep working. 2. Some slave device will die by accident and keep the SDA in low level, in this case, master should send several clock to make the slave release the bus. 3. Slave mode of ESP32 might also get in wrong state that held the SDA low, in this case, master device could send a stop signal to make esp32 slave release the bus. Modifications: 1. Disable I2C_MASTER_TRAN_COMP interrupt to void extra interrupt. 2. Disable un-used timeout interrupt for slave. 3. Add bus reset if error detected for master mode. 4. Add bus clear if SDA level is low when error detected. 5. Modify the argument type of i2c_set_pin. 6. add API to set timeout value 7. add parameter check for timing APIs
This commit is contained in:
@@ -200,7 +200,7 @@ esp_err_t i2c_isr_free(intr_handle_t handle);
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_io_num,
|
||||
esp_err_t i2c_set_pin(i2c_port_t i2c_num, int sda_io_num, int scl_io_num,
|
||||
gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode);
|
||||
|
||||
/**
|
||||
@@ -341,7 +341,7 @@ esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle);
|
||||
* - 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_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, portBASE_TYPE ticks_to_wait);
|
||||
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief I2C slave write data to internal ringbuffer, when tx fifo empty, isr will fill the hardware
|
||||
@@ -358,7 +358,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
|
||||
* - ESP_FAIL(-1) Parameter error
|
||||
* - Others(>=0) The number of data bytes that pushed to the I2C slave buffer.
|
||||
*/
|
||||
int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait);
|
||||
int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data
|
||||
@@ -375,7 +375,7 @@ int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE
|
||||
* - ESP_FAIL(-1) Parameter error
|
||||
* - Others(>=0) The number of data bytes that read from I2C slave buffer.
|
||||
*/
|
||||
int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait);
|
||||
int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief set I2C master clock period
|
||||
@@ -481,6 +481,25 @@ esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time
|
||||
*/
|
||||
esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time);
|
||||
|
||||
/**
|
||||
* @brief set I2C timeout value
|
||||
* @param i2c_num I2C port number
|
||||
* @param timeout timeout value for I2C bus (unit: APB 80Mhz clock cycle)
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t i2c_set_timeout(i2c_port_t i2c_num, int timeout);
|
||||
|
||||
/**
|
||||
* @brief get I2C timeout value
|
||||
* @param i2c_num I2C port number
|
||||
* @param timeout pointer to get timeout value
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t i2c_get_timeout(i2c_port_t i2c_num, int* timeout);
|
||||
/**
|
||||
* @brief set I2C data transfer mode
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user