This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LP-CC2652RB: Erratic IDE behaviour and unable to use I2C with sample project.

Part Number: LP-CC2652RB
Other Parts Discussed in Thread: CC2652RB, SYSCONFIG, Z-STACK, SYSBIOS

I wanted to use the sample project zr_temperaturesensor_LP_CC2652RB_tirtos_ccs and implement my own temperature measuring sensor to try and connect the board with zigbee2mqtt and evaluate a possible new project. I used the BME680 temp sensor with I2C for which i wrote a driver previously and it works fine in a different project on which I can read and write data from the sensor. I can confirm that the communication is correct in terms of protocol compliance, as i've checked it with the oscilloscope. However, this does not work at all when introducing the driver into the sample project as i2c communication seems to fail mid transmission (this only happens when using a sample ztack project) . I will try and detail the steps i took below:

In main, before creating a new task i initialize the i2c peripheral and the sensor. 

#define SPI_BUFFER_SIZE 64
#define I2C_BUS_ADDR 0x00

static struct bme680_dev gBME680Struct = { };
bool gResult = true;
uint16_t gMeasPeriod = 0;

static I2C_Handle gI2C = { 0 };
static I2C_Params gI2CParams = { 0 };
static I2C_Transaction gI2CTransaction = { 0 };
static bool gI2CInitDone = false;

static uint8_t txBuf[SPI_BUFFER_SIZE];
static uint8_t rxBuf[SPI_BUFFER_SIZE];

int8_t userRead(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len);
int8_t userWrite(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len);
void userDelay(uint32_t period);

int8_t sensorBME680Init()
{
    if (!gI2CInitDone){
        return __LINE__;
    }

    int8_t rslt = BME680_OK;

    gBME680Struct.dev_id = BME680_I2C_ADDR_PRIMARY;
    gBME680Struct.intf = BME680_I2C_INTF;
    gBME680Struct.read = userRead;
    gBME680Struct.write = userWrite;
    gBME680Struct.delay_ms = userDelay;
    gBME680Struct.amb_temp = 25;

    rslt = bme680_init(&gBME680Struct);

    if (0 != rslt)
    {
        return __LINE__;
    }

    uint16_t setRequiredSettings;
    /* Set the temperature, pressure and humidity settings */
    gBME680Struct.tph_sett.os_hum = BME680_OS_2X;
    gBME680Struct.tph_sett.os_pres = BME680_OS_4X;
    gBME680Struct.tph_sett.os_temp = BME680_OS_8X;
    gBME680Struct.tph_sett.filter = BME680_FILTER_SIZE_3;
    /* Set the remaining gas sensor settings and link the heating profile */
    gBME680Struct.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
    /* Create a ramp heat waveform in 3 steps */
    gBME680Struct.gas_sett.heatr_temp = 320; /* degree Celsius */
    gBME680Struct.gas_sett.heatr_dur = 150; /* milliseconds */
    /* Select the power mode */
    gBME680Struct.power_mode = BME680_FORCED_MODE;
    /* Must be set before writing the sensor configuration */
    /* Set the required sensor settings needed */
    setRequiredSettings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL
            | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL;
    /* Set the desired sensor configuration */
    rslt = bme680_set_sensor_settings(setRequiredSettings, &gBME680Struct);
    /* Set the power mode */
    rslt = bme680_set_sensor_mode(&gBME680Struct);
    bme680_get_profile_dur(&gMeasPeriod, &gBME680Struct);
    gBME680Struct.delay_ms(gMeasPeriod); /* Delay till the measurement is ready */

    memset(txBuf, 0, SPI_BUFFER_SIZE);
    memset(rxBuf, 0, SPI_BUFFER_SIZE);

    return rslt;
}

int8_t sensorBME680I2CInit()
{
    I2C_init();
    I2C_Params_init(&gI2CParams);
    gI2C = I2C_open(I2C_BUS_ADDR, &gI2CParams);

    if (NULL == gI2C)
    {
        return __LINE__;
    }

    gI2CInitDone = true;

    return 0;
}

int8_t userRead(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len)
{
    if (!gI2CInitDone)
    {
        return __LINE__;
    }

    if ((dev_id != gBME680Struct.dev_id) || (NULL == data))
    {
        return __LINE__;
    }

    memset(txBuf, 0, SPI_BUFFER_SIZE);
    txBuf[0] = reg_addr;

    gI2CTransaction.slaveAddress = dev_id /*<< 1*/;
    gI2CTransaction.readCount = len;
    gI2CTransaction.readBuf = data;
    gI2CTransaction.writeCount = 1;
    gI2CTransaction.writeBuf = txBuf;

    gResult = I2C_transfer(gI2C, &gI2CTransaction);

    if (!gResult)
    {
        return -1;
    }

    return 0;
}

int8_t userWrite(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len)
{
    if (!gI2CInitDone)
    {
        return -1;
    }

    if ((dev_id != gBME680Struct.dev_id) || (NULL == data))
    {
        return -1;
    }

    memset(txBuf, 0, SPI_BUFFER_SIZE);
    txBuf[0] = reg_addr;
    memcpy(&txBuf[1], data, len);

    gI2CTransaction.slaveAddress = dev_id;
    gI2CTransaction.writeCount = len + 1;
    gI2CTransaction.writeBuf = txBuf;
    gI2CTransaction.readCount = 0;

    gResult = I2C_transfer(gI2C, &gI2CTransaction);

    if (!gResult)
    {
        return 1;
    }

    return 0;
}

void userDelay(uint32_t period)
{
    usleep(1000 * period);
}

int8_t getSensorData(struct bme680_field_data *pData)
{
    if (!gI2CInitDone)
    {
        return -2;
    }

    if (NULL == pData)
    {
        return -1;
    }

    static int8_t rslt = 0;
    rslt = bme680_get_sensor_data(pData, &gBME680Struct);

    if (gBME680Struct.power_mode == BME680_FORCED_MODE)
    {
        rslt = bme680_set_sensor_mode(&gBME680Struct);
    }

    return rslt;
}

void sensorBME680CommInit()
{
    int8_t rslt = sensorBME680I2CInit();

    if (0 != rslt)
    {
        return;
    }

    rslt &= sensorBME680Init();

    if (0 == rslt)
    {
        gInitDone = true;
    }
}

Then, i create a new task to poll the sensor and report the temperature changes:

void pollTask()
{
    static int16_t temp = 0;
    static zstack_bdbRepChangedAttrValueReq_t zReq;

    while (1)
    {
        if (gInitDone)
        {
            GPIO_toggle(CONFIG_GPIO_RLED);

            getSensorData(&gZSensorData);
            temp = gZSensorData.temperature;

            if (temp < zclSampleTemperatureSensor_MaxMeasuredValue
                    && temp > zclSampleTemperatureSensor_MinMeasuredValue)
            {

                zclSampleTemperatureSensor_MeasuredValue = temp;
            }
            else if (temp >= zclSampleTemperatureSensor_MaxMeasuredValue)
            {
                zclSampleTemperatureSensor_MeasuredValue =
                        zclSampleTemperatureSensor_MaxMeasuredValue;
            }
            else if (temp <= zclSampleTemperatureSensor_MinMeasuredValue)
            {
                zclSampleTemperatureSensor_MeasuredValue =
                        zclSampleTemperatureSensor_MinMeasuredValue;
            }

#ifdef BDB_REPORTING
            zReq.attrID = ATTRID_TEMPERATURE_MEASUREMENT_MEASURED_VALUE;
            zReq.cluster = ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT;
            zReq.endpoint = SAMPLETEMPERATURESENSOR_ENDPOINT;

            Zstackapi_bdbRepChangedAttrValueReq(appServiceTaskId, &zReq);
#endif

            Task_sleep(100 * (1000 / Clock_tickPeriod));
        }
    }
}

I initialize it in main and start the task:

#define SENSOR_TASK_STACK_SIZE 1000
Task_Struct gPollTask;
Char pollTaskStack[SENSOR_TASK_STACK_SIZE];

Void sensorPollTask(UArg a0, UArg a1)
{
    extern void pollTask();
    pollTask();
}

// in main
{
sensorBME680CommInit();


Task_Params sensorTaskParams;
Task_Params_init(&sensorTaskParams);
sensorTaskParams.stack = pollTaskStack;
sensorTaskParams.priority = 2;
sensorTaskParams.stackSize = SENSOR_TASK_STACK_SIZE;
Task_construct(&gPollTask, sensorPollTask, &sensorTaskParams, NULL);

BIOS_start();
}

I cannot use the debugger for debugging purposes as it jumps all over the place, i can't follow each instruction and neither can i see most variables' values even when i'm paused over a line in scope. I've never had this happen in any ide before. Anyways, i can use the oscilloscope and see that some parts of the code are not being executed, but the program does not seem to fail. To be more clear, the first step in initializing the sensor is a soft reset command via i2c (send address with write bit set, send register address and send register value). The peripheral will only send the slave address and register address (which are both successfully acknowledged by the slave), then abruptly cuts off, does not send the register value nor end the i2c transmission with the stop bit. Will attach photo:

I assume the transmission does not end with a fail, but as i said once the debugger reaches the I2C_Transfer line, it just free runs by itself and will not let me step over to see the transmission result. Code following sensorBME680CommInit(); in main seems to get executed as the main zigbee task is running and the board does not seem to reset. 

As a summary: in main, i call rslt = bme680_init(&gBME680Struct); which in turn calls rslt &= sensorBME680Init(); which calls rslt = bme680_init(&gBME680Struct); that is supposed to send a soft reset command to the sensor. The first two frames of the command are sent and ack'ed, but the transmission ends abruptly while the rest of the code seems to get executed. I cannot, for the life of me, get the board to initialize the sensor and check whether the rest of what i wrote works. Sensor polling task starts, but the sensor was not initialized, thus i cannot validate the data it's supposed to send. 

  • Try to reduce compiler optimization level to debug again.

  • Hello.

    I've reduced optimizations to none but i cannot see any difference. If there are any differences, i still cannot step over the code that was problematic. Even so, what could cause a halt in the middle of an i2c transmission?

  • Hello A V,

    Please clarify for me, does I2C_transfer return, and if so what is the status value?  Have you tried changing the I2C bit clock speed or making the I2C task a higher priority than the Z-Stack task?  Does I2C fail regardless of the connection state of the Zigbee application?  Based on the logic analyzer screenshot it almost appears as though the last SDA ACK came at about the same time as the SCL bit transition and I'm curious as to whether it was missed by the task.  Have you compared screenshots between the working and broken versions?  Also, what I2C SysConfig settings along with SDK and compiler versions are being used?

    Regards,
    Ryan

  • Hi.

    1. The i2c_transfer function does not return. I've delved deeper and it looks like it crashes the program mid transfer (hope it's not some other cause and i'm misinterpreting it) and the rest of the code does not execute. Pausing the debugger after this shows that the CPU is stuck in a forever loop inside ti_sysbios_family_arm_m3_Hwi_excHandler__I() and there is a previous call to strlen() that seemingly attempts to read a lot of invalid addresses. I can't see any other call before strlen.
    2. I've tried using both 100kHz default bitrate as well as 400kHz. I've made the task priority level 1, but it's getting clear that the issue is a crash somewhere.
    3. The program seems to crash regardless of it being connected.
    4. The ack looks fine, i've checked now and there are many other samples of frames where the ack bit came right when the clock went low.
    5. I2C config settings are the default ones, have not changed anything and i'm using I2C_0 sdk version  6.20.00.29

    Seeing this call to strlen followed by a HWI, i looked again at my wrapper over BME680 and I2C to check wheter i might leave some garbage inside any of the buffers, but i can't see anything evident. I tried stepping into the I2C_Transfer function, but the debugger jumps all over the place and can't reliably step over instructions. I can see that the program crashes at line 217 of I2C_transferTimeout in I2C.c . That line is a SemaphoneP_pend, bu it seems that it returns OK.

  • Hi A V,

    Thank you for providing more debug information as it further helps understand what is transpiring.  The m3.Hwi exception can be further investigated using the ROV as instructed in the Z-Stack Debugging Guide.  Once you've confirmed that there is no program or stack overflows, please decode the exception inside ROV -> Hwi -> Exception.  Most often the reason for a bus-fault is that a pointer is not initialized and a function gets the pointer, assumes it’s valid and dereferences the pointer and writes to the invalid address.  Does the issue always occur during the first I2C_transfer?  You can add I2CCC26XX.c/h from source\ti\drivers\i2c directly into your project workspace to further debug transfer functions. You should compare the pointers which are passed through against your working I2C example.

    Regards,
    Ryan

  • Hi Ryan. I've solved this issue myself, and as i suspected it was a stupid mistake rather than an I2C issue. In case anyone else encounters system crashes in conditions similar to mine, i was initializing I2C (which was successful) and then trying to soft reset the sensor by sending a command before allowing BIOS to start. This causes the call to SemaphoreP_pend to fail. I've solved this by initializing I2C and sending the soft reset packets inside the sensor polling task and everything works as expected, including attribute reporting. I'm able to see the BME680 parameters reported correctly within zigbee2mqtt. Thanks for all your help.