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.

TMS320C5535: C55 CSL I2C Read Error

Part Number: TMS320C5535

Hello,

we use C55 CSL on the C5535 DSP and write/read the AIC3254 audio codec via chip support I2C functions. Writing works fine. Reading also works, but after several seconds the I2C bus hangs up. The I2C_Read() function returns CSL_I2C_NACK_ERR and CLK line stays low. When calling I2C_Read() again afterwards, CSL_I2C_BUS_BUSY_ERR is returned. 

CSL_Status I2CBus_Init(Uint32 i2cInClk)
{
  CSL_I2cSetup i2cSetup;
  CSL_Status result;

  // I2C hardware initialization
  result = I2C_init(CSL_I2C0);
  if (result != CSL_SOK)
  {
    return result;
  }

  // I2C setup
  i2cSetup.addrMode = CSL_I2C_ADDR_7BIT;
  i2cSetup.bitCount = CSL_I2C_BC_8BITS;
  i2cSetup.loopBack = CSL_I2C_LOOPBACK_DISABLE;
  i2cSetup.freeMode = CSL_I2C_FREEMODE_ENABLE;
  i2cSetup.repeatMode = CSL_I2C_REPEATMODE_DISABLE;
  i2cSetup.ownAddr = I2C_OWN_ADDR;
  i2cSetup.sysInputClk = i2cInClk;
  i2cSetup.i2cBusFreq = I2C_BUS_FREQ;

  result = I2C_setup(&i2cSetup);
  if (result != CSL_SOK)
  {
    return (result);
  }

  return CSL_SOK;
}

status = AIC3254_Read((Uint16) 0, (Uint16*) &value);
if (status != CSL_SOK)
{
   printf("ERROR; %d\n", status);
}

CSL_Status AIC3254_Read(Uint16 regAddr, Uint16 *data)
{

  return (I2C_Read(I2C_CODEC_ADDR, regAddr, data));

}

CSL_Status I2C_Read(Uint16 slaveAddr, Uint16 regAddr, Uint16 *data)

{
  volatile Uint16 looper;
  CSL_Status status = CSL_ESYS_FAIL;
  Uint16 readCount = 1;
  Uint16 readBuff[1];
  Uint16 startStop = ((CSL_I2C_START) | (CSL_I2C_STOP));

  regAddr = (regAddr & 0x00FF);

  // read the data
  status = I2C_read(readBuff, readCount, slaveAddr, &regAddr, 1, TRUE,
  startStop, CSL_I2C_MAX_TIMEOUT>>4, FALSE);
  if (status != CSL_SOK)
  {
    return status;
  }

  *data = readBuff[0];


  for (looper = 0; looper < CSL_I2C_MAX_TIMEOUT>>4; looper++)
  {
  ;
  }

  return status;

}

It seems as if the codec doesn't send the ack. How can we recover from this situation, so that the I2C bus can be used again?

Thanks in advance and have a nice day

Marc

  • Hi Marc,

    When I2C_read() function return CSL_I2C_NACK_ERR the I2C slave did not generate an acknowledge as it is written in csl_i2c.h file. I'll try to explain this with more details:
    If a slave device detects an address match, it will send an ACK by driving SDA low during the next clock cycle; if no slave recognizes the address then the SDA line will be left alone to be pulled up high.  Following a successful ACK, data will be either sent to the slave device or read from the slave device (depending on what was indicated by the Read/Write bit).  Therefore, each byte is 9 bits: either 7 address plus one R/W plus one ACK/NAK, or 8 data plus one ACK/NAK.  The last data byte of a transaction should generally be followed by a NAK, to indicate that it is intended to be the final byte.  After this, either a STOP or a ReSTART should be issued by the Master.
    Therefore you should analyze whether the NAK is coming after the last expected byte or NAK comes earlier when you expect to receive more data.
    In both cases you should restart I2C otherwise the slave will respond with Busy.
    I suggest you to try to stop and start or re-start the I2C bus using CSL_I2C_SETSTOP() and CSL_I2C_SETSTART() or CSL_I2C_RESETSTART().

    You can find more details about I2C communication and troubleshooting in linked guides:

    Regards,
    Tsvetolin Shulev

  • Hello, 

    thanks a lot for your explanation. We had a closer look at the read process. Now the read is terminated with a Read Timeout while polling for the ICRRDY Bit in the CSL I2C Read function. 

    The following screenshot shows a good read. The AIC3254 codec has slave address 0x18. Register 0x41 is read and contains data 0x32.

    The next screenshot shows an erroneous read. After the write of the register address, the I2C peripheral doesn't start the read sequence. This leads to a timeout error.

    The next screenshot also shows a timeout error. This time only the slave address is sent in the read sequence. The write bit is set instead of the read bit when the second slave address is sent.

    All the screenshot are initiated by the call of the i2c read function in the C55 CSL. This is really strange behaviour. It looks as if the i2c peripheral doesn't work correctly? Are there any known issues?

    Thanks a lot and have a nice day

    Marc

  • Hello,

    we fixed the issue by changing the order of 2 lines in the I2C_Read() function in the CSL. Since we use DSP BIOS, the I2C task can be interrupted at any time by ISRs or other higher priority tasks. We saw that the order of initializing the write transfer in the I2C_read() function is different than in the I2C_Write() function. We removed the blue lines and inserted them at another position (red). Thus setting start or stop bit is done when all other initializations are finished.

    With this fix, we have no more I2C errors. 

    CSL_Status I2C_read(Uint16 *i2cRdBuf,
    Uint16 dataLength,
    Uint16 slaveAddr,
    Uint16 *subAddr,
    Uint16 subAddrLength,
    Bool masterMode,
    Uint16 startStopFlag,
    Uint16 timeout,
    Bool checkBus)
    {
    volatile Uint16 looper;
    Uint16 dataCount;
    Uint16 statusByte;
    ///CSL_Status status;
    Bool writeSubAddr;

    writeSubAddr = (subAddrLength > 0)?TRUE:FALSE;

    if((i2cRdBuf != NULL) && (dataLength !=0))
    {
    if ((subAddr != NULL) && (writeSubAddr == TRUE)) /* Write the Sub Address */
    {
    /* check for bus busy */
    for(looper = 0; looper < timeout; looper++)
    {
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR, I2C_ICSTR_BB);
    if(statusByte == FALSE)
    {
    break;
    }
    }

    if(looper >= timeout)
    {
    /* bus busy timeout error */
    return(CSL_I2C_BUS_BUSY_ERR);
    }

    /* Set the Tx mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_TRX, SET);

    /* Set the data length */
    CSL_FINS(i2cHandle->i2cRegs->ICCNT, I2C_ICCNT_ICDC, (subAddrLength));

    if(masterMode == TRUE)
    {
    /* Set the slave address */
    CSL_FINS(i2cHandle->i2cRegs->ICSAR, I2C_ICSAR_SADDR, slaveAddr);

    /* Enable Master mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_MST, SET);

    /* Set the start bit */
    if((startStopFlag & CSL_I2C_START) == CSL_I2C_START)
    {
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_STT, SET);
    }
    }
    else
    {
    /* Disable Master mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_MST, CLEAR);
    }

    /* Set the Tx mode */
    // CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_TRX, SET);

    /* Set the data length */
    // CSL_FINS(i2cHandle->i2cRegs->ICCNT, I2C_ICCNT_ICDC, (subAddrLength));

    for(dataCount = 0; dataCount < subAddrLength; dataCount++)
    {
    /* Check for ICXRDY status */
    for(looper = 0; looper < timeout; looper++)
    {
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR,
    I2C_ICSTR_ICXRDY);
    if(statusByte == TRUE)
    {
    break;
    }
    }

    if(looper >= timeout)
    {
    return(CSL_I2C_TIMEOUT_ERROR);
    }

    /* Write data to the data Tx register */
    CSL_FINS(i2cHandle->i2cRegs->ICDXR, I2C_ICDXR_D, *subAddr++);

    for(looper = 0; looper < timeout; looper++)
    {
    /* Check for NACK status */
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR,
    I2C_ICSTR_NACK);
    if(statusByte == FALSE)
    {
    break;
    }
    }

    if(looper >= timeout)
    {
    return(CSL_I2C_NACK_ERR);
    }
    }
    /* Give some delay */
    for(looper = 0; looper < timeout; looper++){;}
    }

    /* Set the Rx mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_TRX, CLEAR);

    /* Set the data length */
    CSL_FINS(i2cHandle->i2cRegs->ICCNT, I2C_ICCNT_ICDC, dataLength);

    if(masterMode == TRUE)
    {
    /* Set the slave address */
    CSL_FINS(i2cHandle->i2cRegs->ICSAR, I2C_ICSAR_SADDR, slaveAddr);

    /* Enable Master mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_MST, SET);

    /* Set the start bit */
    if((startStopFlag & CSL_I2C_START) == CSL_I2C_START)
    {
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_STT, SET);
    }
    }
    else
    {
    /* Disable Master mode */
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_MST, CLEAR);
    }

    if(checkBus == TRUE)
    {
    /* check for bus busy */
    for(looper = 0; looper < timeout; looper++)
    {
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR, I2C_ICSTR_BB);
    if (statusByte == FALSE)
    {
    break;
    }
    }

    if(looper >= timeout)
    {
    /* bus busy timeout error */
    return(CSL_I2C_BUS_BUSY_ERR);
    }
    }

    for(dataCount = 0; dataCount < dataLength; dataCount++)
    {
    /* Check for ICRRDY status */
    for(looper = 0; looper < timeout; looper++)
    {
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR,
    I2C_ICSTR_ICRRDY);
    if(statusByte == TRUE)
    {
    break;
    }
    }

    if(looper >= timeout)
    {
    return(CSL_I2C_TIMEOUT_ERROR);
    }

    /* Read data from the data Rx register */
    *i2cRdBuf++ = CSL_FEXT(i2cHandle->i2cRegs->ICDRR, I2C_ICDRR_D);

    /* Check for Overflow status */
    statusByte = CSL_FEXT(i2cHandle->i2cRegs->ICSTR, I2C_ICSTR_RSFULL);
    if(statusByte == TRUE)
    {
    return(CSL_I2C_RECEIVE_OVERFLOW_ERR);
    }
    }
    }
    else
    {
    return(CSL_ESYS_INVPARAMS);
    }

    /* Set the stop bit */
    if((startStopFlag & CSL_I2C_STOP) == CSL_I2C_STOP)
    {
    CSL_FINST(i2cHandle->i2cRegs->ICMDR, I2C_ICMDR_STP, SET);
    }

    return(CSL_SOK);
    }

  • Hello,

    one final question concerning I2C error handling.

    Is there a general error handling method which can be applied when the I2C_Read() or I2C_Write() CSL functions return an error? We don't want to check and handle each error separately. The bus should always stay active.

    Thanks in advance

    Marc

  • Marc,

    Thank you for sharing the solution. Unfortunately there is no CSL global I2C handling mechanism.
    I hope you can find useful TMS320C55x Chip Support LibraryAPI Reference Guide chapter 10 describing the I2C module, lists the API structure, functions, and macros within the module, and provides an I2C API reference section.
    www.ti.com/.../spru433j.pdf

    Regards,
    Tsvetolin Shulev