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.

CCS/TMS320F28069M: I2C issue getting data from AM4096

Part Number: TMS320F28069M

Tool/software: Code Composer Studio

Hello all,

I am trying to get data from a magnetic sensor (PN:AM4096) and I'm having issues with it. The code worked well with a temperature sensor on the same bus. Essencially, I get valid data during the first time, but when I try a second time, the code hangs looking for I2caRegs.I2CMDR.bit.STT flag to clear in read_bytes function.

Uint16 AM4096class::GetAbsoluteAngle(void)
{
    Uint8 data[2];
    int16 result = I2C_SUCCESS;

    AM4096class::SetRegisterAddress(32);
    result = I2C.read_bytes((AM4096_ADDRESS_CODE | (AM4096class::Address)) , sizeof(data), data);
    AM4096class::DATA_union.DATA_Registers.DATA_ADDR_33.all = data[1]<<8+data[0];
    return result;
}

Uint16 I2Cclass::read_bytes(Uint16 slave_address, Uint16 count, Uint8 *out)
{
    Uint16 i, ret;
    GpioDataRegs.GPADAT.bit.GPIO21 = 0;
    I2Cclass::start(slave_address);
    GpioDataRegs.GPADAT.bit.GPIO21 = 1;

    // issue repeated start
    I2caRegs.I2CMDR.bit.STT = 1;

    // wait for repeated start to end
    I2Cclass::timeout=0;
    while (I2caRegs.I2CMDR.bit.STT != 0)
    {
        I2Cclass::I2Cclass::timeout++;
        if(I2Cclass::I2Cclass::timeout>TIMEOUT_LIMIT)
        {
            return I2C_ERROR;
        }
    }

    // non-repeat mode
    I2caRegs.I2CMDR.bit.RM = 0;

    // set data count
    I2caRegs.I2CCNT = count;

    // generate STOP condition when the data counter counts down to 0
    I2caRegs.I2CMDR.bit.STP = 1;

    // receiver mode
    I2caRegs.I2CMDR.bit.TRX = 0;

    for (i = 0; i < count; ++i)
    {
        // wait until the data receive register is ready to be read
        I2Cclass::timeout=0;
        while (I2caRegs.I2CSTR.bit.RRDY != 1)
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;

        }
        out[i] = I2caRegs.I2CDRR;
    }

    return I2C_SUCCESS;
}
Uint16 I2Cclass::start(Uint16 slave_address)
{
    // load slave address
    I2caRegs.I2CSAR = slave_address;

    // wait for STOP condition
    I2Cclass::timeout=0;
    while (I2caRegs.I2CMDR.bit.STP != 0)
    {
        I2Cclass::timeout++;
        if(I2Cclass::timeout>TIMEOUT_LIMIT)
            return I2C_ERROR;

    }

    // master mode
    I2caRegs.I2CMDR.bit.MST = 1;

    // generate START condition
    I2caRegs.I2CMDR.bit.STT = 1;

    return I2C_SUCCESS;
}

This picture below is the first request. It returns the correct value.

This second image shows the I2C bus hanging. It's able to send the slave and register address.

This image is when running the sensor from an arduino. It works every time.

Could someone please shine some light on this? Thank you!

  • Can you try checking the ARDY bit before reconfiguring the I2C for the repeated start like what is suggested on the following wiki page?

    https://processors.wiki.ti.com/index.php/I2C_Tips#Repeated_Start

    Whitney

  • Here is the plot with ARDY on channel 3 (updated every 1us). Looks like ARDY never clears after the first packet is complete. Do you think I have another byte in the buffer?

  • I don't get stuck if I clear ARDY at the beggining of my function: 

    Uint16 I2Cclass::read_bytes(Uint16 slave_address, Uint16 count, Uint8 *out)
    {
        Uint16 i, ret;
        Uint8 junk[16];
    
    
        I2caRegs.I2CSTR.bit.ARDY = 1;
    
        I2Cclass::start(slave_address);

    Don't think this would be a solution though. Why is ARDY not cleared at the end of the first packet?

  • To clarify, you should be waiting for ARDY to be set, not cleared. When it's set, it indicates the registers are ready to be updated because their previous configuration has been used. So is ARDY not going high again or is it just cut off in your most recent screen capture?

    Whitney

  • Got it. The issue is that ARDY never clears after a successful transaction (STOP bit issued)

  • i've read again and again the link mentioned and no luck. I re-wrote the code to match what's proposed and I still get stuck when checking for ARDY.

    Uint16 I2Cclass::read_block(Uint16 slave_address, Uint16 read_address, Uint16 count, Uint8 *out)
    {
        Uint16 i, ret;
    
        I2Cclass::timeout=0;
        while(I2caRegs.I2CSTR.bit.BB);
    
        I2caRegs.I2CMDR.all = 0;    //disable I2C module during configuation
    
        I2caRegs.I2CSAR = slave_address;    //Setup device address
    
        I2caRegs.I2CMDR.all = 0x2620;
    
        I2Cclass::timeout=0;
        while( (!I2caRegs.I2CSTR.bit.XRDY));// || (!I2caRegs.I2CSTR.bit.ARDY));
    
        if ( I2caRegs.I2CSTR.bit.NACK )
        {
            I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
            return I2C_NACK_ERROR;
        }
        I2caRegs.I2CDXR = read_address;     //Write register address
    
        I2Cclass::timeout=0;
    //***I GET STUCK HERE!***//
        while(!I2caRegs.I2CSTR.bit.ARDY); //Wait until ARDY clears
    
        // set data count
        I2caRegs.I2CCNT = count;
    
        I2caRegs.I2CMDR.all = 0x2C20;
    
        for (i = 0; i < count; ++i)
        {
            // wait until the data receive register is ready to be read
            I2Cclass::timeout=0;
            while( (!I2caRegs.I2CSTR.bit.RRDY) || (!I2caRegs.I2CSTR.bit.ARDY));
            if ( I2caRegs.I2CSTR.bit.NACK )
            {
                I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
                return I2C_NACK_ERROR;
            }
            out[i] = I2caRegs.I2CDRR;
        }
        return I2C_SUCCESS;
    }
    

    The data below shows some of the control lines I'm monitoring.

  • When I2CMDR.RM is 0, the ARDY won't go high again until the byte count counts down to zero--but you never set the byte count before sending the read address. If you set the byte count to 1, does ARDY go high at the appropriate time?

    Whitney

  • Hi Whitney,

    I found that issue. I have the code now working, but it will lock up if I check for ARDY in the two spots below. However, they are in the reference code you've mentioned.

    Uint16 I2Cclass::read_block(Uint16 slave_address, Uint16 read_address, Uint16 count, Uint8 *out)
    {
        Uint16 i;
    
        I2Cclass::timeout=0;
        while(I2caRegs.I2CSTR.bit.BB);
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        I2caRegs.I2CMDR.all = 0;
        I2caRegs.I2CSAR = slave_address;    //Setup device address
        I2caRegs.I2CCNT = 1;                //Send only one byte (register address)
        I2caRegs.I2CMDR.all = 0x2620;       //start transmission
    
    
        I2Cclass::timeout=0;
        //<<<THE CODE LOCKS UP HERE IF I CHECK FOR ARDY>>>
        while( (!I2caRegs.I2CSTR.bit.XRDY));// || (!I2caRegs.I2CSTR.bit.ARDY));
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        if ( I2caRegs.I2CSTR.bit.NACK )
        {
            I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
            return I2C_NACK_ERROR;
        }
        I2caRegs.I2CDXR = read_address;     //Write register address
    
        I2Cclass::timeout=0;
        while(!I2caRegs.I2CSTR.bit.ARDY); //Wait until ARDY clears
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        // set data count
        I2caRegs.I2CCNT = count;
    
        I2caRegs.I2CMDR.all = 0x2C20;
    
        for (i = 0; i < count; ++i)
        {
            // wait until the data receive register is ready to be read
            I2Cclass::timeout=0;
            //<<<THE CODE LOCKS UP HERE IF I CHECK FOR ARDY>>>
            while( (!I2caRegs.I2CSTR.bit.RRDY));// || (!I2caRegs.I2CSTR.bit.ARDY));
            {
                I2Cclass::timeout++;
                if(I2Cclass::timeout>TIMEOUT_LIMIT)
                    return I2C_ERROR;
    
            }
            if ( I2caRegs.I2CSTR.bit.NACK )
            {
                I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
                return I2C_NACK_ERROR;
            }
            out[i] = I2caRegs.I2CDRR;
        }
        return I2C_SUCCESS;
    }
    

    Here is how the signals look like in the signal analyzer.

  • Hi Fabio,

    For the section you're saying gets stuck below, you are ORing these statements correct? I can't see why it would get stuck if so.

        //<<<THE CODE LOCKS UP HERE IF I CHECK FOR ARDY>>>
        while( (!I2caRegs.I2CSTR.bit.XRDY));// || (!I2caRegs.I2CSTR.bit.ARDY));
    Maybe try also enabling the FREE MODE bit in your I2CMDR line before. This appears to be a difference in your code, but I'm not certain why that would change anything.
    I2caRegs.I2CMDR.all = 0x4620;       //start transmission
    Best,
    Kevin
  • That actually makes it worse. The code will lock up all the time at:

        I2caRegs.I2CDXR = read_address;     //Write register address
    
        I2Cclass::timeout=0;
        while(!I2caRegs.I2CSTR.bit.ARDY); //Wait until ARDY clears
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }

  • So the statement when it isn't commented out is this:

    while((!I2caRegs.I2CSTR.bit.XRDY) || (!I2caRegs.I2CSTR.bit.ARDY));

    Isn't this waiting until both XRDY and ARDY are set before breaking out of the loop? I think the example code is saying to wait for either one.

    while(!(I2caRegs.I2CSTR.bit.XRDY || I2caRegs.I2CSTR.bit.ARDY));

    Also, did forget to remove the ; when you added the timeout code is is that intentional?

    Whitney

  • Hi Whitney,

    Thanks for catching the ; error. It was not intentional, but it shouldn't affect the issue I have.

    The while statement you propose makes sense logically, but I don't quite understand the statement in the reference guide that I have to wait for ARDY to clear then. When running this code, I can run now without locking up. However, ARDY is not always cleared since XRDY is the the flag breaking this while loop.

  • Here is a copy of my latest code for reference:

    Uint16 I2Cclass::read_block(Uint16 slave_address, Uint16 read_address, Uint16 count, Uint8 *out)
    {
        Uint16 i;
    
        I2Cclass::timeout=0;
        while(I2caRegs.I2CSTR.bit.BB)
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        I2caRegs.I2CMDR.all = 0;
        I2caRegs.I2CSAR = slave_address;    //Setup device address
        I2caRegs.I2CCNT = 1;                //Send only one byte (register address)
        I2caRegs.I2CMDR.all = 0x2620;       //start transmission
    
    
        I2Cclass::timeout=0;
        //<<<THE CODE LOCKS UP HERE IF I CHECK FOR ARDY>>>
        while( !(I2caRegs.I2CSTR.bit.XRDY || I2caRegs.I2CSTR.bit.ARDY))
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        if ( I2caRegs.I2CSTR.bit.NACK )
        {
            I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
            return I2C_NACK_ERROR;
        }
        I2caRegs.I2CDXR = read_address;     //Write register address
    
        I2Cclass::timeout=0;
        while(!I2caRegs.I2CSTR.bit.ARDY) //Wait until ARDY clears
        {
            I2Cclass::timeout++;
            if(I2Cclass::timeout>TIMEOUT_LIMIT)
                return I2C_ERROR;
        }
    
        // set data count
        I2caRegs.I2CCNT = count;
    
        I2caRegs.I2CMDR.all = 0x2C20;
    
        for (i = 0; i < count; ++i)
        {
            // wait until the data receive register is ready to be read
            I2Cclass::timeout=0;
            //<<<THE CODE LOCKS UP HERE IF I CHECK FOR ARDY>>>
            while( !(I2caRegs.I2CSTR.bit.RRDY || I2caRegs.I2CSTR.bit.ARDY))
            {
                I2Cclass::timeout++;
                if(I2Cclass::timeout>TIMEOUT_LIMIT)
                    return I2C_ERROR;
    
            }
            if ( I2caRegs.I2CSTR.bit.NACK )
            {
                I2caRegs.I2CMDR.all = 0; // reset I2C so SCL isn't held low return RRET__FAIL;
                return I2C_NACK_ERROR;
            }
            out[i] = I2caRegs.I2CDRR;
        }
        return I2C_SUCCESS;
    }

  • If the last screen capture you shared is still valid after your code updates, I think the ARDY behavior is as expected. You see it go high after the read address is sent because STP = 0. You still need to check for it in the other loops to break you out in case of a NACK, but it doesn't necessarily mean something is wrong if it doesn't get set.

    Do you agree or do you think there's still an issue?

    Whitney

  • OK. Thanks. I think I'm good for now. Appreciate the help!