(solved) I2C device poses a challenge

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

(solved) I2C device poses a challenge

Postby mzimmers » Fri Jan 04, 2019 11:58 pm

Hi all -

I'm trying to integrate a Sensirion SHT20 thermometer/hygrometer to the STM32. It uses a standard I2C interface, but with a wrinkle: after I send it a command to give me the temp (or humidity), I have to insert a delay before I attempt to read the reply. From the SHT20 datasheet (sorry this is kind of long):
There are two different operation modes to communicate with the sensor: Hold Master mode or No Hold Master mode. In the first case the SCL line is blocked (controlled by sensor) during measurement process while in the latter case the SCL line remains open for other communication while the sensor is processing the measurement. No hold master mode allows for processing other I2C communication tasks on a bus while the sensor is measuring. A communication sequence of the two modes is displayed in Figure 15 and Figure 16, respectively.
In the hold master mode, the SHT2x pulls down the SCL line while measuring to force the master into a wait state. By releasing the SCL line the sensor indicates that internal processing is terminated and that transmission may be continued.
In no hold master mode, the MCU has to poll for the termination of the internal processing of the sensor. This is done by sending a Start condition followed by the I2C header (1000’0001) as shown in Figure 16. If the internal processing is finished, the sensor acknowledges the poll of the MCU and data can be read by the MCU. If the measurement processing is not finished the sensor answers no ACK bit and the Start condition must be issued once more.
When using the no hold master mode it is recommended to include a wait period of 20 μs after the reception of the sensor’s ACK bit (bit 18 in Figure 16) and before the Stop condition.
I've verified that I'm talking with the device, by reading its user register (this doesn't require a delay). Regarding a temp read, here's what I've tried with no success:

Code: Select all

int Thermometer::thermRead(uint8_t cmd, uint8_t *data, uint16_t len)
{
    int rc;

    m_i2c_cmd = i2c_cmd_link_create();
    //ESP_LOGI(TAG, "i2cReadReg(): m_i2c_cmd is %x.", (unsigned) m_i2c_cmd);
    ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
    ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_WRITE, true));
    ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, cmd, true));
    ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
    ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_READ, true));
//    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, len, I2C_MASTER_LAST_NACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_NACK));
    ESP_ERROR_CHECK(i2c_master_stop(m_i2c_cmd));
    rc = (i2c_master_cmd_begin(I2C_PORT_NBR, m_i2c_cmd, I2C_WAITTIME_TICKS));
I get a TIMEOUT error on this.

Can anyone see a way to do this using the I2C library, or does it look like I'm going to have to bit-bang this one?

Thanks...
Last edited by mzimmers on Wed Jan 09, 2019 10:17 pm, edited 1 time in total.

vonnieda
Posts: 145
Joined: Tue Nov 07, 2017 3:42 pm

Re: I2C device poses a challenge

Postby vonnieda » Sat Jan 05, 2019 1:43 am

Just perform the write in it's entirety, delay, then perform the read. Two separate transactions.

Like this:

Code: Select all

int rc;

m_i2c_cmd = i2c_cmd_link_create();
ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_WRITE, true));
ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, cmd, true));
ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_PORT_NBR, m_i2c_cmd, I2C_WAITTIME_TICKS));
ESP_ERROR_CHECK(i2c_cmd_link_delete(m_i2c_cmd));

vTaskDelay(pdMS_TO_TICKS(10));

m_i2c_cmd = i2c_cmd_link_create();
ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_READ, true));
ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_NACK));
ESP_ERROR_CHECK(i2c_master_stop(m_i2c_cmd));
rc = (i2c_master_cmd_begin(I2C_PORT_NBR, m_i2c_cmd, I2C_WAITTIME_TICKS));
ESP_ERROR_CHECK(i2c_cmd_link_delete(m_i2c_cmd));
Jason

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: I2C device poses a challenge

Postby mzimmers » Sat Jan 05, 2019 3:38 pm

Hi Jason -

I'm not sure that will work. I tried it (using a couple different delay values) and the second part still fails.

If I'm reading the datasheet correctly, I think the ESP32 has to be prepared to begin accepting data from the SHT20 as soon as the SCL line is released...agree? If this is true, we can't impose an arbitrary delay or we risk missing incoming data. (At least that's how I see it, but I'm hardly an I2C expert.)

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: I2C device poses a challenge

Postby WiFive » Sun Jan 06, 2019 1:39 am

mzimmers wrote:
Sat Jan 05, 2019 3:38 pm

If I'm reading the datasheet correctly, I think the ESP32 has to be prepared to begin accepting data from the SHT20 as soon as the SCL line is released...agree?
In the Hold Master mode, so don't use that mode. Otherwise you will have to use some of the patches floating around GitHub to support long clock stretching

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: I2C device poses a challenge

Postby mzimmers » Sun Jan 06, 2019 2:21 am

I don't understand what the ESP32 is supposed to do after the first stop condition. The SHT20 needs time to complete its measurement. Do I need to poll the GPIO that the SCL is on?
Attachments
Image 1-5-19 at 6.13 PM.jpg
Image 1-5-19 at 6.13 PM.jpg (88.33 KiB) Viewed 18537 times

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: I2C device poses a challenge

Postby WiFive » Sun Jan 06, 2019 8:09 am

There is a maximum measurement time in the datasheet, so just vtaskdelay for at least that long. If you need to optimize you can consider other options.

fivdiAtESP32
Posts: 47
Joined: Thu Dec 20, 2018 9:47 am

Re: I2C device poses a challenge

Postby fivdiAtESP32 » Sun Jan 06, 2019 11:09 am

What I don't understand is how to wait 20 microseconds directly before the first stop condition.

You don't need to poll the GPIO that the SCL is on.

If I understand things correctly, after the first stop condition (which is the P after the 20 microsecond delay in the above diagram,) the idea is to attempt to read three bytes from the device in a loop until the three bytes can be read successfully. I say attempt here because it will keep failing while the device is still performing the measurement.

Another point is that it looks like there is a bug in the following code as each read will store the data read in the same byte:

Code: Select all

    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, (uint8_t *) data, 1, I2C_MASTER_NACK));
Something like this is needed:

Code: Select all

    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[0], 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[1], 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[2], 1, I2C_MASTER_NACK));

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: I2C device poses a challenge

Postby mzimmers » Sun Jan 06, 2019 3:37 pm

When I broke the sequence into two parts (per Jason's suggestion), the first one succeeds. So, that 20us delay doesn't seem absolutely necessary.

I agree that a loop would be in order - keep sending the read byte until you get an ACK - but I don't know how to do that from the library.

fivdiAtESP32
Posts: 47
Joined: Thu Dec 20, 2018 9:47 am

Re: I2C device poses a challenge

Postby fivdiAtESP32 » Sun Jan 06, 2019 4:41 pm

Would something like the following not work?

Code: Select all

  m_i2c_cmd = i2c_cmd_link_create();
  ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
  ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_WRITE, true));
  ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, cmd, true));
  ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_PORT_NBR, m_i2c_cmd, I2C_WAITTIME_TICKS));
  ESP_ERROR_CHECK(i2c_cmd_link_delete(m_i2c_cmd));

  for (int i = 0; i <= 100; ++i) {
    vTaskDelay(pdMS_TO_TICKS(10));

    m_i2c_cmd = i2c_cmd_link_create();
    ESP_ERROR_CHECK(i2c_master_start(m_i2c_cmd));
    ESP_ERROR_CHECK(i2c_master_write_byte(m_i2c_cmd, SHT20_ADDR_READ, true));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[0], 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[1], 1, I2C_MASTER_ACK));
    ESP_ERROR_CHECK(i2c_master_read(m_i2c_cmd, &data[2], 1, I2C_MASTER_NACK));
    ESP_ERROR_CHECK(i2c_master_stop(m_i2c_cmd));
    rc = i2c_master_cmd_begin(I2C_PORT_NBR, m_i2c_cmd, I2C_WAITTIME_TICKS);
    i2c_cmd_link_delete(m_i2c_cmd);

    if (rc == ESP_OK) {
      printf("Excellent, it worked\n");
      break;
    } else if (rc == ESP_FAIL) {
      printf("Error sending command, device didn't ACK, lets try again\n");
    } else if (rc == ESP_ERR_TIMEOUT) {
      printf("Operation timeout because the bus is busy, lets try again\n");
      printf("It may be a better idea to bail out here\n");
    } else {
      printf("Some other error, bail out\n");
      ESP_ERROR_CHECK(rc);
    }
  }

  if (rc == ESP_OK) {
    printf("Excellent, it worked\n");
  } else {
    printf("It didn't work, bail out\n");
    ESP_ERROR_CHECK(rc);
  }
 

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: I2C device poses a challenge

Postby mzimmers » Sun Jan 06, 2019 5:07 pm

I tried it; it just timed out each iteration.

I also tried separating the write of the read address from the reads; basically the same thing happened.

Who is online

Users browsing this forum: Google [Bot] and 107 guests