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.

CC26xx I2C Blocking Read Failure

Other Parts Discussed in Thread: CC2640

We are using the TiRTOS I2C Driver on the CC2640.  

Every now and then when we call a I2C_Transfer( ... ) as a read we feel like the driver is crashing.  We see the driver send the slave address and then the slave clock stretches for about 7ms... clock returns high but does not continue to clock in data from slave.

This same I2C_Transfer works 99% of the time but will randomly have the issue described above.  

We set up a timer to detect the failure and attempt to reset the I2C driver by calling I2C_Close followed by I2C_Open.  This does not seem to fix the problem.  

We also noticed that the task calling the I2C_Transfer is in an unknown state when the error occurs.

Has anyone seen this or know if there is a solution... or at least know the proper way to reset the driver?

Thanks,

Ryan

  • Hi Ryan,

    I'm not aware of this issue, but we'll have our driver team look at it. Can you reproduce it on the SmartRF06EB + CC2650EM?
    Also, do you observe any RTOS stack or CSTACK over flows?

    Best wishes
  • i have not seen a RTOS stack or CSTACK overflow yet. like i said earlier we did see the task that called the transfer in an unknown state when it crashes.

    I should update you on our progress.

    We substituted our own I2C transfer routine (authored by following the flow chart in the user guide) and have not seen the issue pop back up. We ran over 1000 cycles on our tester and never saw our driver crash... when using the TI driver it would crash at random within 500 cycles.

    Ryan
  • Hi Ryan,

    Thanks for your update. I've provided your feedback to our driver team and will let you know of any feedback.

    Best wishes
  • Hello Ryan,

    The problem may be from an overflow error as the TI driver exist the i2c routine to service other interrupts. Have you done a side by side comparison between your drive and TI to see where the discrepancy is?

    Thanks,
  • I have not done a side by side comparison. Can you?

    Below is our working implementation of the I2C read and write. These functions have ran thousands of times without any error opposed to the TI transfer function that runs up to a few hundred times before it crashes.

    One thing to note is that we do not do any power handling in the functions... do you recommend we try to add the power handing back in to ours, i'm hesitant that it might be related to the problem?

    Also, we are finding that with our implementation we are not able to send just 1 byte, hence the patch changing the size to 2... do you know why that be?

    Let me know what you think or find,
    Ryan


    // ASSUMTIONS:
    // I2C already configured
    static bool I2C_Send( const uint8_t *data, uint16_t size )
    {
    bool success = true;

    // Patch for a known issue when only sending 1 byte
    if (size == 1)
    {
    size = 2;
    }

    // Clear the receive bit (leaving the slave address alone)
    MSA = CP_SLAVE_ADDRESS << 1;

    // Write the first byte...
    MDR = *data++;
    size--;

    // Initiate the transfer
    MCTRL = I2C_MASTER_CMD_BURST_SEND_START;


    while(1)
    {
    // Wait for byte transfer. On timeout, set return value to false
    if (I2C_Wait() == false)
    {
    MCTRL = I2C_MASTER_CMD_BURST_SEND_ERROR_STOP;
    success = false;
    break;
    }

    // Bail on error
    if(MSTAT & I2C_MSTAT_ERR)
    {
    success = false;

    // Be sure we stop things if an arbitration error is found...
    if(MSTAT & I2C_MSTAT_ARBLST)
    {
    MCTRL = I2C_MASTER_CMD_BURST_SEND_ERROR_STOP;
    }

    break;
    }

    // Write the next byte...
    MDR = *data++;

    // If this is the last, we do things a bit differently...
    if(--size == 0)
    {
    break;
    }

    MCTRL = I2C_MASTER_CMD_BURST_SEND_CONT;
    }

    // If we exited the while without error...
    if(success == true)
    {
    MCTRL = I2C_MASTER_CMD_BURST_SEND_FINISH;

    if ( I2C_Wait() == true)
    {
    // Final check for errors
    if(MSTAT & I2C_MSTAT_ERR)
    {
    success = false;
    }
    }
    else
    {
    MCTRL = I2C_MASTER_CMD_BURST_SEND_ERROR_STOP;
    success = false;
    }
    }

    return success;
    }

    static uint16_t I2C_Receive( uint8_t *data, uint16_t size )
    {
    int offset = 0;
    int i;

    for(i=0; i<size; i++)
    {
    data[i] = 0;
    }

    // Set the receive bit
    MSA = (CP_SLAVE_ADDRESS << 1) | I2C_MSA_RS;

    // Initiate the receive
    MCTRL = I2C_MASTER_CMD_BURST_RECEIVE_START;


    while(1)
    {
    // Wait for byte transfer. On timeout, set return value to false
    if (I2C_Wait() == false)
    {
    return offset;
    }

    // Bail on error
    if(MSTAT & I2C_MSTAT_ERR)
    {
    // If we lost arbitration, also ensure we issue a stop
    if(MSTAT & I2C_MSTAT_ARBLST)
    {
    MCTRL = I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP;
    }

    return offset;
    }

    // Grab the icomming data
    data[offset++] = MDR;

    // If we have a single byte remaining, continue below...
    if(--size == 1 )
    {
    break;
    }

    // This lets us bail out if an error condition is triggering a shorter-than-expected message length.
    // if(data[0] == 0x42)
    // {
    // break;
    // }

    // Prep for next...
    MCTRL = I2C_MASTER_CMD_BURST_RECEIVE_CONT;

    }

    // Signal that this is the last...
    MCTRL = I2C_MASTER_CMD_BURST_RECEIVE_FINISH;

    // wait for transfer
    if (I2C_Wait() == true)
    {
    // Final check for errors
    if(MSTAT & I2C_MSTAT_ERR)
    {
    return offset;
    }
    else
    {
    // grab the last byte
    data[offset++] = MDR;
    }
    }

    return offset;
    }
  • Hello Ryan,

    I will look at the code and compare when I get a chance.

    For you size problem, you are doing more than one decrement of the size value.  Plus you are doing it in pre and post mode, that is, the size-- will decrement after (post) the value is taken and then you have it in the (pre) condition of --size where the value is decremented before the condition is met.  You are exiting with the break condition right away when you have a value of 1.

    Thanks,