AnsweredAssumed Answered

End I2C (TWI) read

Question asked by alevy on Jul 23, 2013
Latest reply on Aug 23, 2013 by MaheshN

Hi-

 

I have an I2C sensor connected to an ADSP-21479.  The sensor datasheet says to do the following:

 

  1. Start an acquisition by writing a control register, followed by a stop condition
  2. Write the address of a status register to the device.  Don't issue a stop condition, use a repeated start for step 3.
  3. Read the status register
  4. If bit 0 of the status register is 1, go to step 5.  Else, repeat step 3.
  5. Issue another repeated start condition, write the data register address to read
  6. Issue another repeated start condition, read the data
  7. Go back to step 1

 

The attached screenshot shows around step 4.  The green dots are I2C start conditions, the red dot is an I2C stop condition  The DSP has read several status bytes that indicate the data is not ready at this point.  The data then changes to 0x19, indicating the data is ready.  The next action should be to start writing.  Instead, it reads one more byte of data.  This adds an extra cycle of latency, but is otherwise benign.  A larger problem is when the DSP attempts to write the data register address in step 5, it send the chip's address, but it never actually sends the data register address to read.  Then, the DSP generates a stop condition followed immediately by a start condition for the read in step 6.  Please look at the following code and tell me where I'm going wrong (please note: I can't copy/paste from my editor for some reason, it formats the code as tables and such, so there may be a few typos in here).

 

void I2CISR(int SigNum)
{

int IRQSrc;

// Already wrote the control reg outside the ISR, start off writing the status reg
static int State = STATE_WRITE_STATUS_REG;

    iIRQSrc = *pTWIIRPTL;     // Read the RW1C register to find the source of the IRQ

    if (iIRQSrc & TWIMERR) {
        // We've had an error.  Don't really care why, restart the whole process
        State = STATE_WRITE_CTRL_REG;
    }

    switch (State) {
        case STATE_WRITE_CTRL_REG:
            // Only get here if we've had an error.  Restart everything.
            InitI2CWrite(0x30,     // Chip address
                                  0x07,     // Data byte 1, control register address
                                  0x01,     // Data byte 2, register value
                                  2,           // Count
                                  I2C_16_BIT_DATA | I2C_XFER_COM_IRQ_EN);     // I2C Conditions
            State = STATE_WRITE_STATUS_REG;
            break;

        case STATE_WRITE_STATUS_REG:
            InitI2CWrite(0x30,     // Chip address
                                  0x06,     // Data byte 1, status register address
                                  0,           // Data byte 2, unused
                                  1,           // Count
                                  I2C_REPEAT_START | I2C_XFER_COM_IRQ_EN);     // I2C conditions
            State = STATE_READ_CALL;
            break;

        case STATE_READ_CALL:
            InitI2CRead(0x30, // Chip address
                                   0xFF, // Count set to 0xFF means keep taking data, don't count
                                   I2C_BUF_IRQ_EN | I2C_XFER_COM_IRQ_EN);     // I2C Conditions
            State = STATE_CHECK_STATUS_REG;
            break;

        case STATE_CHECK_STATUS_REG:
            // Check if the "data ready" bit is set
            if (*pRXTWI8 & 1) {
                InitI2CWrite(0x30,     // Chip address
                                      0x00,     // Data byte to write
                                      1,        // Count
                                       I2C_REPEAT_START | I2C_XFER_COM_IRQ_EN);     // I2C Conditions

                if (iIRQSrc & TWICOM) {
                    // Update to write data read state.  Only expect to get here
                    // if we've held off entering this ISR due to a different
                    // ISR running when the IRQ fires.
                    State = STATE_START_DATA_READ;
                } else {
                    // Update to wait for Tx complete state
                    State = STATE_WAIT_FOR_TX_COMPLETE;
                 }
            } else {
                 // Status is "not ready," try again.
                 ContinueI2CRead(0xFF);
            }
            break;

        case STATE_WAIT_FOR_TX_COMPLETE:
            // This is just here to clear the IRQ that results from the 
            // preceding transfer, we don't need to do anything, the new
            // transfer has already been initiated.
            State = STATE_START_DATA_READ;
            break;

        case STATE_START_DATA_READ:
            InitI2CRead(0x30,     // Chip address
                                  6,            // Count
                                  I2C_16_BIT_DATA | I2C_REPEAT_START | I2C_BUF_IRQ_EN | I2C_XFER_COM_IRQ_EN);     // I2C conditions
            State = STATE_READ_DATA;
            break;

        case STATE_READ_DATA:
            ...
            break;
    }

    *pTWIIRPTL = iIRQSrc;

    return;
}

void InitI2CWrite(int DeviceAddr, int Data1, int Data2, int Count, int I2CCond)
{
    int IRQMask =  TWIMERR;

    *pTWIMADDR = DeviceAddr;

    if (I2CCond & I2C_16_BIT_DATA) {
        *pTXTWI8 = Data1;
        *pTXTWI8 = Data2;
        *pTWIFIFOCTL = TWITXINT2 | TWIBHD;
    } else {
        *pTXTWI8 = Data1;
        *pTWIFIFOCTL = TWIBHD;
    }

    if (I2CCond & I2C_BUF_IRQ_EN) {
        IRQMask |= TWITXINT;
    }
    if (I2CCond & I2C_XFER_COM_IRQ_EN) {
        IRQMask |= TWIMCOM;
    }
    *pTWIIMASK = IRQMask;

    if (I2CCond & I2C_REPEAT_START) { 
        *pTWIMCTL = (Count << 6) | TWIMEN | TWIRSTART;
    } else {
        *pTWIMCTL = (Count << 6) | TWIMEN;
    }

}

void InitI2CRead(int DeviceAddr, int Count, int I2CCond)
{
    int IRQMask =  TWIMERR;

    *pTWIMADDR = DeviceAddr;

    if (I2CCond & I2C_16_BIT_DATA) {
        *pTWIFIFOCTL = TWIRXINT2 | TWIBHD;
    } else {
        *pTWIFIFOCTL = TWIBHD;
    }

    if (I2CCond & I2C_BUF_IRQ_EN) {
        IRQMask |= TWIRXINT;
    }
    if (I2CCond & I2C_XFER_COM_IRQ_EN) {
        IRQMask |= TWIMCOM;
    }
    *pTWIIMASK = IRQMask;

    if (I2CCond & I2C_REPEAT_START) { 
        *pTWIMCTL = (Count << 6) | TWIMEN | TWIMDIR | TWIRSTART;
    } else {
        *pTWIMCTL = (Count << 6) | TWIMEN | TWIMDIR;
    }

}

void ContinueI2CRead(int Count)
{
    int Temp;
    Temp = *pTWIMCTL;

#define TWI_COUNT_BITS (0xFF << 6)

    // If the count is set to 0xFF, it never decrements.  Don't overwrite
    // that particular value, it was set up like that for a reason
    if (Count != 0xFF) {
        Temp &= ~TWI_COUNT_BITS;
        Temp |= Count << 6;
        *pTWIMCTL = Temp;
    }
}

Attachments

Outcomes