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.

CC2541: random disconnection when using i2c slave

Part Number: CC2541

I am using the BLE 1.4.2 stack on the CC2541 device (actually a JDY-08 module). When enabling the i2c slave driver using interrupts the bluetooth connection suddenly drops in the first few minutes. The I2C slave is only accessed for writing 3 bytes every 1 minute for 16 events every 1 second. Looking at the I2C protocol the data is not clock stretched. There is no other I2C traffic on the bus. There is a similar question here in the forum, but the thread is locked and I cannot see any resolution to the problem e2e.ti.com/.../571254

Anybody out there using the i2c slave mode successfully?

Thanks
Kai 

  • I have tracked down the issue to the SLV_READ() macro inside hal_i2c.c

    This receive loop blocks the device forever if something goes odd. So I have modified it to my needs. Since I know the specific timing of the I2C master on the bus, I have introduced a timeout of about 5ms with this modification of the code. I have no clue why the receiving function fails in the first place.

    // Stop clock-stretching and then read when it arrives.
    #define SLV_READ(_X_) st ({         \
      volatile int timeout = 0x300;     \
      I2CCFG &= ~I2C_SI;                \
      while (((I2CCFG & I2C_SI) == 0)   \
        &&    (I2CSTAT != slvStopped)   \
        && timeout--);                  \
      (_X_) = I2CDATA;                  \
    })

    The above workaround mittigates the issue to the app level, where the data failure needs to be handled on a higher protocol level.

    -Kai

  • Hi Kai,

    Good catch! Thanks for letting us know you resolved the problem.

    Cheers,
    Fredrik
  • With this workaround the Bluetooth Connection keeps alive. However I am still fighting that the i2c protocol is thrown off. So I digged deeper into the issue and found, that the clock stretch is the evil here. When the CC2541 is in slave mode and the master writes data to the slave (slave read mode) the first thing after the device write address is that the cc2541 holds down the clock, issues an interrupt and the ISR routine then releases the clock line. My master is unfortunately a dump device just spitting out the i2c clock and data pulses, no clock stretching possible. Most of the time (>97%) this works fine, since the ISR routine is fast enough with responding. However when it fails the first 2 clock pulses (ca 400uS) gets cut and results in a NACK. This is all understood and reasonable. Not all patterns result in a NACK and therefore I am getting occasionally wrong data.

    Since I am not able to reliably detect false data, I wish to improve the situation. So one main question here is how can I avoid the clock stretching. Is there a configuration I am missing here? I thought that a clock stretch is only required when the receiving shift register is not empty, but after decoding the device write address this should not be the case. Any ideas here how to avoid this situation?

    Thanks
    Kai

    PS: I figured out that the I2CDATA register seems to have some residues before the interface got enabled. This caused the clock stretch right after the device address phase. So I am missing a flush when the interface gets enabled. I modified the init code as following introducing the dummy variable and issuing a read in the I2CDATA register:

    void HalI2CInit(uint8 address, i2cCallback_t i2cCallback)
    {
      volatile uint8 dummy;
      i2cCB = i2cCallback;
      i2cRxLen = 0;
      i2cTxLen = 0;
      HAL_ASSERT(i2cCB);
    
      I2C_WRAPPER_DISABLE();
      I2CADDR = address << 1;
      I2C_ENABLE();
      // Clear the CPU interrupt flag for Port_2 PxIFG has to be cleared before PxIF.
      I2C_PXIFG = 0;
      I2C_IF = 0;
      dummy = I2CDATA; //clear receive register
      I2C_INT_ENABLE();
    }
    

  • ... and further down the I2C road ... the clock stretch is due to the AA setting, so an interrupt is generated after the device address is recognised. As long as the interrupt is pending and the bit is not cleared the clock stretch happens. The i2c HAL driver jumps into the receiving routine for the entire i2c telegram, but enables the HAL_ENTER_ISR() macro. In ordert o avoid the clock stretch, I surrounded the ISR routine with HAL_ENTER_CRITICAL_SECTION() and HAL_EXIT_CRITICAL_SECTION replacing HAL_ENTER_ISR() and HAL_EXIT_ISR(). This will lock out any other interrupt during the entire i2c telegram period, so as a system designer you need to be aware not to utilise the i2c bus for extended time. In my case it is very 1 second <5ms and this does not conflict with the bluetooth connection (experimental, not by conformance).

    I wonder if there is any method of utilising I2C transfers in combination with DMA to avoid this conflicts. Is this possible?

    BR
    Kai