Very strange I2C behaviour when reading a byte with NACK
Posted: Wed Jul 26, 2017 5:17 pm
I've been trying to convert a program from the ESP Arduino core to a C version which reads accelerometer data via I2C. The Ardunio version works fine but I am struggling to get the C version to behave correctly. The problem is when reading bytes from the bus. The relevant code is:
Notice in particular this line:
As far as I understand this is the correct way to read a single byte from the I2C bus. However it fails to return successfully. If I switch the NACK_VAL to an ACK_VAL however (which is incorrect because we only want to read a single byte), it does successfully read the value. Debugging with a logic analyser, we can see a little bit what is going on. It appears that when I ask the driver to read a byte with a NACK bit, the SCL clock simply stops.
Not working with NACK: Kind of working but not really with ACK: I have no idea why this is happening but it is very reliable. Also, the original Arduino version is able to send a NACK bit with no problems and the SCL clock continues to run smoothly. Any ideas what I am doing wrong or what I might try next to continue debugging?
Full code listing along with outputs, more screenshots and datatraces are here: https://github.com/alexspurling/lis3dh
Code: Select all
/**
* Read register
*/
uint8_t read_reg(uint8_t reg_addr)
{
// Specify register to read
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte (cmd, (ACCEL<<1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte (cmd, reg_addr, ACK_CHECK_EN);
esp_err_t ret = i2c_master_cmd_begin (I2C_MASTER_NUM, cmd, 100/ portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
printf("Error writing i2c address byte %d. Ret: %d\n", reg_addr, ret);
} else {
printf("Successfully wrote i2c address byte %d. Ret: %d\n", reg_addr, ret);
}
// Now read a byte from that register
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte (cmd, (ACCEL<<1) | I2C_MASTER_READ, ACK_CHECK_EN);
uint8_t res = 0;
i2c_master_read_byte (cmd, &res, NACK_VAL);
ret = i2c_master_cmd_begin (I2C_MASTER_NUM, cmd, 100/ portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
printf("Error reading register %d: %d. Ret: %d\n", reg_addr, res, ret);
} else {
printf("Successfully read register %d: %d. Ret: %d\n", reg_addr, res, ret);
}
return res;
}
Code: Select all
i2c_master_read_byte (cmd, &res, NACK_VAL);
Not working with NACK: Kind of working but not really with ACK: I have no idea why this is happening but it is very reliable. Also, the original Arduino version is able to send a NACK bit with no problems and the SCL clock continues to run smoothly. Any ideas what I am doing wrong or what I might try next to continue debugging?
Full code listing along with outputs, more screenshots and datatraces are here: https://github.com/alexspurling/lis3dh