I2C polling and ACK/NACK checked in next command

marekg
Posts: 10
Joined: Thu Apr 18, 2019 2:08 pm

I2C polling and ACK/NACK checked in next command

Postby marekg » Tue May 07, 2019 12:39 pm

Dear all,
I am struggling with I2C bus and reading measurement from SHT30 sensor.
I am able to read serial number and status register therefore i2c driver, i2c bus and sensor should be working.
But for measurement readout in polling mode I need to create special sequence to "poll" if measurement is ready or not.
I create this sequence exactly according SHT30 specification and example source code.

Here is my readout code snippet:

Code: Select all

static esp_err_t sh2_start_measurement_with_polling_ht_sensor(void)
{
        uint8_t data_rd[6];
        esp_err_t err_return;
        memset(data_rd, 0, 6);
        uint16_t temperature_readout = 0;
        uint16_t humidity_readout = 0;

        i2c_port_t i2c_port_num = I2C_MASTER_NUM;

        //change default I2C timeout to higher value (workaround)
        i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        

        //create new i2c link for i2c commands
        i2c_cmd_handle_t i2c_command_handle = i2c_cmd_link_create();

        //queue start bit
        if ( sh2_handle_i2C_error_return ( i2c_master_start(i2c_command_handle)) != ESP_OK  ) return ESP_FAIL;
        //queue address with ACK checking
        i2c_master_write_byte(i2c_command_handle, (HT_SENSOR_I2C_7B_ADDRESS << 1) | I2C_WRITE_BIT, ACK_CHECK_EN);
        //queue command with ACK checking
        i2c_master_write_byte(i2c_command_handle, (CMD_MEAS_POLLING_M >> 8), ACK_CHECK_EN);
        //queue command with ACK checking
        i2c_master_write_byte(i2c_command_handle, (CMD_MEAS_POLLING_M & 0xff), ACK_CHECK_EN);
        //begin i2c command
        i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
        //delete link to release i2c resources
        i2c_cmd_link_delete(i2c_command_handle);
        vTaskDelay(1 / portTICK_PERIOD_MS);

        //poll command with reading device address until ACK appears (this means that measurement has been finished)
        uint8_t reading_tries = MAX_SHT_POLLING_TRIES;
        while(reading_tries--)
        {
            //change default I2C timeout to higher value (workaround)
            i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        
            //create new i2c link for i2c commands
            i2c_command_handle = i2c_cmd_link_create();
            //queue start bit
            i2c_master_start(i2c_command_handle);
            //queue address with ACK checking
            i2c_master_write_byte(i2c_command_handle, (HT_SENSOR_I2C_7B_ADDRESS << 1) | I2C_READ_BIT, ACK_CHECK_EN) ;
            //begin i2c command
            err_return = i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
            //delete link to release i2c resources
            i2c_cmd_link_delete(i2c_command_handle);
            if ( err_return == ESP_OK  ) break;

            //wait little bit and cycle reading again
            vTaskDelay(1 / portTICK_PERIOD_MS);
        }

        //if no error then finish reading of temperature and humidity raw values and CRCs
        if (reading_tries > 0)
        {
            //change default I2C timeout to higher value (workaround)
            i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        
            //create new i2c link for i2c commands
            i2c_command_handle = i2c_cmd_link_create();
            //queue data read command with ACK checking
            i2c_master_read(i2c_command_handle, data_rd, 6, I2C_MASTER_LAST_NACK );
            //queue stop bit
            i2c_master_stop(i2c_command_handle);
            //begin i2c command
            err_return =  i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
            //delete link to release i2c resources
            i2c_cmd_link_delete(i2c_command_handle);

            //verify CRC of received TEMPERATURE data - it is feature of SHT30
            err_return = sh2_check_crc(data_rd, 2, data_rd + 2);                                    //check CRC of first 2 bytes
            if (err_return != ESP_OK)
            {
                device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_CRC;
                return ESP_FAIL;
            }
            temperature_readout = (data_rd[0] << 24) | (data_rd[1] << 16);                        //copy data of temperature readout
            device_data.ht_sensor_data.sensor_temperature = temperature_readout;

            //verify CRC of received HUMIDITY data - it is feature of SHT30
            err_return = sh2_check_crc(data_rd + 3, 2, data_rd + 5);                            //check CRC of first 2 bytes
            if (err_return != ESP_OK)
            {
                device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_CRC;
                return ESP_FAIL;
            }
            humidity_readout = (data_rd[3] << 8) | (data_rd[4] );                               //copy data of humidity readout
            device_data.ht_sensor_data.sensor_humidity = humidity_readout;
            return ESP_OK;
        }
        else
        {
            //there was problem while trying to read SHT sensor
            device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_FATAL;
            return ESP_FAIL;
        }
}
1. My first question is following:
Why ACK/NACK checking are sent in next command cycle?
See this picture for better view. Position of cursor shows ACK checking. This checking is sent after first 1ms delay in code.
sht30_i2c_readout_with_polling.PNG
sht30_i2c_readout_with_polling.PNG (68.63 KiB) Viewed 509 times
2. My second question is, if there is something wrong in my code, because it is not working at all.
Is there any way how to make polling functional?
How to force I2C driver to send ACK/NACK checking in one command stream?

Thank you for your help.

marekg
Posts: 10
Joined: Thu Apr 18, 2019 2:08 pm

Re: I2C polling and ACK/NACK checked in next command

Postby marekg » Thu May 09, 2019 6:00 am

Hello,

I would like to add some notes to the case.
In debugging mode, when I jump over line:

Code: Select all

 i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
there is no ACK/NACK checking clock. This ACK/NACK clock appears in the next cycle of:

Code: Select all

 i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
Any idea what could be the reason?

marekg
Posts: 10
Joined: Thu Apr 18, 2019 2:08 pm

Re: I2C polling and ACK/NACK checked in next command

Postby marekg » Thu May 09, 2019 6:08 am

Hello,

in the meantime, I wrote next function to read sensor in periodic measurement mode.
In this case, master sends command to put sensor to periodic reading.
Next, master poll if new measurement is ready.
Below is code snippet which basically works and is able to read values from the sensor.
But, there is other issue which I do not understand, what is the reason.
After reading of value from the sensor, master needs to send STOP command to release I2C bus.
In the code example, it is commented part of code. This code causes delay around 10seconds.
But STOP command is sent imediately without any delay. This is really weird.
Any idea what could be the reason, please?

Code: Select all

static esp_err_t sh2_start_periodic_measurement_with_polling_ht_sensor(void)
{
        uint8_t data_rd[6];
        esp_err_t err_return;
        //uint8_t *data_rd = (uint8_t *)malloc(6);
        memset(data_rd, 0, 6);
        uint16_t temperature_readout = 0;
        uint16_t humidity_readout = 0;

        //first send command to start periodic measurement
        i2c_port_t i2c_port_num = I2C_MASTER_NUM;
        //change default I2C timeout to higher value (workaround)
        i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        // (!) this is really necessary otherwise I2C reading finished with timeout error () see ESP32 forums (e.g. https://github.com/espressif/esp-idf/issues/680)
        //create new i2c link for i2c commands
        i2c_cmd_handle_t i2c_command_handle = i2c_cmd_link_create();
        //queue start bit
        if ( sh2_handle_i2C_error_return ( i2c_master_start(i2c_command_handle)) != ESP_OK  ) return ESP_FAIL;
        //queue address with ACK checking
        i2c_master_write_byte(i2c_command_handle, (HT_SENSOR_I2C_7B_ADDRESS << 1) | I2C_WRITE_BIT, ACK_CHECK_EN);
        //queue command with ACK checking
        i2c_master_write_byte(i2c_command_handle, (CMD_MEAS_PERI_10_M >> 8), ACK_CHECK_EN);
        //queue command with ACK checking
        i2c_master_write_byte(i2c_command_handle, (CMD_MEAS_PERI_10_M & 0xff), ACK_CHECK_EN);
        //queue stop bit
        i2c_master_stop(i2c_command_handle);
        //begin i2c command
        i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
        //delete link to release i2c resources
        i2c_cmd_link_delete(i2c_command_handle);

        //poll command with reading device address until ACK appears (this means that measurement has been finished)
        uint8_t reading_tries = MAX_SHT_POLLING_TRIES;
        while(reading_tries--)
        {
            //change default I2C timeout to higher value (workaround)
            i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        // (!) this is really necessary otherwise I2C reading finished with timeout error () see ESP32 forums (e.g. https://github.com/espressif/esp-idf/issues/680)
            //create new i2c link for i2c commands
            i2c_command_handle = i2c_cmd_link_create();
            //queue start bit
            i2c_master_start(i2c_command_handle);
            //queue address with ACK checking
            i2c_master_write_byte(i2c_command_handle, (HT_SENSOR_I2C_7B_ADDRESS << 1) | I2C_WRITE_BIT, ACK_CHECK_EN);
            //queue command with ACK checking
            i2c_master_write_byte(i2c_command_handle, (CMD_FETCH_DATA >> 8), ACK_CHECK_EN);
            //queue command with ACK checking
            i2c_master_write_byte(i2c_command_handle, (CMD_FETCH_DATA & 0xff), ACK_CHECK_EN);
            //queue start bit
            i2c_master_start(i2c_command_handle);
            //queue address with ACK checking
            i2c_master_write_byte(i2c_command_handle, (HT_SENSOR_I2C_7B_ADDRESS << 1) | I2C_READ_BIT, ACK_CHECK_EN);
            //queue data read command with ACK checking
            i2c_master_read(i2c_command_handle, data_rd, 6, I2C_MASTER_LAST_NACK );
            //begin i2c command
            err_return = i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
            //delete link to release i2c resources
            i2c_cmd_link_delete(i2c_command_handle);
            if ( err_return == ESP_OK  ) break;

            //wait little bit and cycle reading again
            vTaskDelay(1 / portTICK_PERIOD_MS);
        }

        /*this commented section causes 10s delay ///////////////////////////////////////////////////////
        //release I2C bus
        //change default I2C timeout to higher value (workaround)
        i2c_set_timeout(i2c_port_num, I2C_MASTER_TOUT_CNUM);        // (!) this is really necessary otherwise I2C reading finished with timeout error () see ESP32 forums (e.g. https://github.com/espressif/esp-idf/issues/680)
        //create new i2c link for i2c commands
        i2c_command_handle = i2c_cmd_link_create();
        //queue stop bit
        i2c_master_stop(i2c_command_handle);
        //begin i2c command
        i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
        //delete link to release i2c resources
        i2c_cmd_link_delete(i2c_command_handle);
        */  /////////////////////////////////////////////////////////////////////////////////////////////

        //if no error then finish reading of temperature and humidity raw values and CRCs
        if (reading_tries > 0)
        {
            //verify CRC of received TEMPERATURE data - it is feature of SHT30
            err_return = sh2_check_crc(data_rd, 2, data_rd + 2);                                    //check CRC of first 2 bytes
            if (err_return != ESP_OK)
            {
                device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_CRC;
                return ESP_FAIL;
            }
            temperature_readout = (data_rd[0] << 8) | (data_rd[1]);                        //copy data of temperature readout
            //device_data.ht_sensor_data.sensor_temperature = temperature_readout;

            //verify CRC of received HUMIDITY data - it is feature of SHT30
            err_return = sh2_check_crc(data_rd + 3, 2, data_rd + 5);                            //check CRC of first 2 bytes
            if (err_return != ESP_OK)
            {
                device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_CRC;
                return ESP_FAIL;
            }
            humidity_readout = (data_rd[3] << 8) | (data_rd[4] );                               //copy data of humidity readout
            //device_data.ht_sensor_data.sensor_humidity = humidity_readout;
            sh2_calculate_temperature_and_humidity(temperature_readout, humidity_readout);
            return ESP_OK;
        }
        else
        {
            //there was problem while trying to read SHT sensor
            device_data.ht_sensor_data.i2c_bus_errors_return = I2C_ERROR_FATAL;
            return ESP_FAIL;
        }
}

marekg
Posts: 10
Joined: Thu Apr 18, 2019 2:08 pm

Re: I2C polling and ACK/NACK checked in next command

Postby marekg » Thu May 09, 2019 10:04 am

Hello,

I found some posts that is there is only one command in the command queue, this results to timeout. Is that true?
This looks to be my case as well.
I am talking about such code:

Code: Select all

        //create new i2c link for i2c commands
        i2c_command_handle = i2c_cmd_link_create();
        //queue stop bit
        i2c_master_stop(i2c_command_handle);
        //begin i2c command
        i2c_master_cmd_begin(i2c_port_num, i2c_command_handle, I2C_READ_WAIT_MS / portTICK_RATE_MS);
        //delete link to release i2c resources
        i2c_cmd_link_delete(i2c_command_handle);
Is there any way how to fix it?
Thank you for help.

IIXII78
Posts: 1
Joined: Wed Jun 19, 2019 9:03 am

Re: I2C polling and ACK/NACK checked in next command

Postby IIXII78 » Thu Aug 01, 2019 1:46 pm

I can confirm this behaviour, the clock pulse for the ACK bit is indeed only sent together with the next command. The bit isn't even evaluated for the last command in a sequence, so there is no error returned even if the ACK check should fail.

This could be called a hardware bug in the Command Controller of the ESP32 I2C peripheral, but it was probably designed with the assumption that every command sequence always ends with a STOP condition, so that any outstanding ACK is always processed.

Who is online

Users browsing this forum: No registered users and 26 guests