esp-idf: I2C ACK error counter makes the next i2c session timeout (IDFGH-8295)
Answers checklist.
- I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
- I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
- I have searched the issue tracker for a similar issue and not found a similar issue.
General issue report
What I want to do
My application is going to check if a slave device is plugged to the I2C bus by send a write byte operation. My expectation is that if there is no ack from slave after the app calling the address, the app gets to know there is not the specific slave device plugged.
What is the issue
The issue is that after the bus get a I2C_STATUS_ACK_ERROR, the SCL wire is pull down to GND. It makes the next I2C session occurs a timeout error. And only after then the I2C SCL can recover to high level and the bus can properly work. In addition, if there is no more i2c communication after a I2C_STATUS_ACK_ERROR, SCL line is going to be low level permanently.
Maybe I found the cause of the issue, but I’m not sure
I found that there is a RESET COUNTER mechanism dealing with the I2C_STATUS_ACK_ERROR, which is in driver/i2c.c, line 1153 to 1161. The Macro I2C_ACKERR_CNT_MAX, as shown below, is defined as 10 in the same file, which means that only if I2C_STATUS_ACK_ERROR appears 10 times, i2c hardware FSM can be reset. And I think that is why the i2c session next to a I2C_STATUS_ACK_ERROR is always timeout.
} else if (p_i2c->status == I2C_STATUS_ACK_ERROR) {
clear_bus_cnt[i2c_num]++;
if (clear_bus_cnt[i2c_num] >= I2C_ACKERR_CNT_MAX) {
clear_bus_cnt[i2c_num] = 0;
i2c_hw_fsm_reset(i2c_num);
}
ret = ESP_FAIL;
} else {
I changed this part to:
} else if (p_i2c->status == I2C_STATUS_ACK_ERROR) {
clear_bus_cnt[i2c_num]++;
if (clear_bus_cnt[i2c_num] >= I2C_ACKERR_CNT_MAX) {
clear_bus_cnt[i2c_num] = 0;
}
i2c_hw_fsm_reset(i2c_num);
ret = ESP_FAIL;
} else {
in which i totally ignore the RESET COUNTER by reset the FSM whenever I2C_STATUS_ACK_ERROR comes out. And this solve the problem.
What I want to ask
What is the RESET COUNTER for? I wonder whether it is fine to simply reset FSM once a ACK ERROR occurs. Will it cause any other problem with out RESET COUNTER mechanism?
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 17 (10 by maintainers)
Commits related to this issue
- i2c: fix a bug in sda sample timing * Closes https://github.com/espressif/esp-idf/issues/9777 This bug prevented SCL line to work properly after a NACK was received in master mode. — committed to espressif/esp-idf by o-marshmallow a year ago
- i2c: fix a bug in sda sample timing * Closes https://github.com/espressif/esp-idf/issues/9777 This bug prevented SCL line to work properly after a NACK was received in master mode. — committed to espressif/esp-idf by o-marshmallow a year ago
- i2c: fix a bug in sda sample timing * Closes https://github.com/espressif/esp-idf/issues/9777 This bug prevented SCL line to work properly after a NACK was received in master mode. — committed to espressif/esp-idf by o-marshmallow a year ago
- i2c: fix a bug in sda sample timing * Closes https://github.com/espressif/esp-idf/issues/9777 This bug prevented SCL line to work properly after a NACK was received in master mode. — committed to espressif/esp-idf by o-marshmallow a year ago
Hi @KonssnoK ,
Thanks for the details, after testing on my side, I was able to reproduce the issue and it seems like it is a problem with the driver indeed. The problem is that after a NACK, the I2C hardware controller doesn’t issue a STOP, which blocks the I2C bus. This is why in your case, resetting the bus after each NACK works. But this is a bit overkill and very (CPU) time consuming. The simplest and fastest solution is to force a STOP signal after NACK is received. Here is a patch that does exactly that. Please try the following patch: i2c_nack.diff.txt
You can apply it with:
(This patch needs to be applied on top of IDF v4.4(.x) versions, which is your case)
If this works for you, please inform me and I will create an internal merge request to fix it globally.