TC38x I2C interface basically uncontrollable in software

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
userTC389
Level 1
Level 1
First reply posted First question asked Welcome!

Let's be honest: The I2C Interface of the TC3xy-family is an overcomplicated mess. Most semiconductor manufacturers would implement a simple shift register and then add FIFOs, burst requests and DMA but Infineon did it the other way round and then nailed the that overcomplicated byte-, half-word- or word-aligned FIFO cludge onto something that could be mistaken for an I2C bus logic.

The manual is basically useless, excessively describes state changes, i2c assymmetric timing alignment but fails to describe the registers sufficiently, is mostly obscure, uses multiple terms for (basically) the same thing without explaining them properly (bytes, characters, packets, bursts - so what DOES an i2c interface transmit then?) but fails to explain one, ONE single simple use case such as "receive 2 bytes from an i2c slave".

The sample code Infineon publishes on GitHub is exactly that: A good example of what code should _not_ look like in the year 2021 when it claims to have been written and i'm not even sure whether it actually works out of Infineon's sample code scope.

I basically got it to work and manage to transmit a given number of bytes to an i2c slave (a real-time clock), but fail to receive a specified number of bytes. It always reads the amount of bytes that fit into the FIFO. The "BREQ" interrupt flag cannot be reset, the "RX"-interrupt flag is never set despite the fact that it is apparently receiving data from the slave and storing it into the FIFO. The hardware doesn't care what i write into the MRPS register and RPS is stuck at "8".  Since my code apparently cannot synchronize to what's going on on the i2c bus, i fail to set "SETEND" at the correct occassion (and the Infineon sample code doesn't set it where the manual specifies it to be set either). I can also not debug properly because the I2C hardware refuses to be controlled and when using SPEN, it goes into "disabled" state while debugging (DISS is "set" then) rendering my debugger's interface completely useless.

I am thinking about reverting to a bit-banging driver i wrote for a C166 derivative almost 20 years ago. But before i do, please help me and explain to me:

How can i manage to receive a given number of bytes (like "1")?
What do i need to do to reset the BREQ interrupt flag?
What is MRPS actually for?
What exactly _IS_ a burst request and how do i service it in software properly?

My software is simply meant to read out time/date during initialisation, long before any sort of interrupt/DMA configuration is set up. Because it only reads a couple of bytes, the runtime is not really an issue, but i simply cannot get it to work.

Many thanks in advance for any support and many greetings,

Oliver

 

0 Likes
7 Replies
MoD
Employee
Employee
50 likes received 500 replies posted 100 solutions authored

Please check the attached code, should be help to have a better understanding:

/* define the FIFO behavior, must be configured accordingly in FIFOCFG */
#define TX_FIFO_ALIGNMENT 0x0          /**< \brief TX Fifo Alignment = Byte */
#define TX_BURST_SIZE     0x2          /**< \brief TX Burst Size = 4 Words */
#define RX_FIFO_ALIGNMENT 0x0          /**< \brief RX Fifo Alignment = Byte */
#define RX_BURST_SIZE     0x2          /**< \brief RX Burst Size = 4 Words */
#if (TX_BURST_SIZE==0 && RX_BURST_SIZE==0)
#define CR_BEHAVIOR       0x1          /**< \brief Automatically data request clear */
#else
#define CR_BEHAVIOR       0x0          /**< \brief Data request clear by software */
#endif

/*!
 * \brief   read a byte from specific eeprom and address
 *
 * ...
 */
uint8 eeprom_read_bytes(uint8 address, uint8 *pData, uint8 count)
{
    uint8  ucCnt, ucBytesRead;
    uint32 status, uiData;

    ucBytesRead = 0;

    /* we expect that we are in the LISTENING state */
    /* write the address to eeprom */
    I2C0_TPSCTRL.U = 0x2;
    /* wait until any request will be raised */
    do {
        status = I2C0_RIS.U;
    } while (status == 0);
#if TX_FIFO_ALIGNMENT == 2
    I2C0_TXD.U = (CHIP_ADDRESS<<1);
#if TX_BURST_SIZE == 0 && CR_BEHAVIOR == 0
    /* requests are not cleared automatically */
    /* we clear the request */
    I2C0_ICR.U = (status & 0xF);
#endif
    I2C0_TXD.U = address;
#else
    I2C0_TXD.U = (address<<8) | (CHIP_ADDRESS<<1);
#endif
#if CR_BEHAVIOR == 0
    /* requests are not cleared automatically */
    /* we clear the request */
    I2C0_ICR.U = (status & 0xF);
#endif
    /* wait for Protocol or error interrupt */
    /* wait until any request will be raised */
    while (I2C0_RIS.U == 0);
    if (I2C0_RIS.U & (0x1<<IFX_I2C_ISR_I2C_ERR_INT_OFF))
    {
        __debug();
        /* clear the FIFO error(s) */
        I2C0_ERRIRQSC.U = I2C0_ERRIRQSS.U;
        /* min. TX_END is also set, needed to clear also */
        I2C0_PIRQSC.U = I2C0_PIRQSS.U;
        return ucBytesRead;
    }
    if (I2C0_PIRQSS.U != (0x1<<IFX_I2C_PIRQSS_TX_END_OFF))
    {
        __debug();
        /* we get another status set as only TX_END, there is an error */
        /* clear the error(s) and return */
        I2C0_PIRQSC.U = I2C0_PIRQSS.U;
        return ucBytesRead;
    }
    /* clear the TX_END bit */
    I2C0_PIRQSC.B.TX_END = 1;
    /* we are now in LISTENING or MASTER RESTART state */
    /* write the chip address to eeprom */
    I2C0_TPSCTRL.U = 0x1;
    /* write the byte to read */
    I2C0_MRPSCTRL.U = count;
    /* wait until any request will be raised */
    do {
        status = I2C0_RIS.U;
    } while (status == 0);
    I2C0_TXD.U = (CHIP_ADDRESS<<1) | 0x1;
#if CR_BEHAVIOR == 0
    /* requests are not cleared automatically */
    /* we clear raised request(s) */
    I2C0_ICR.U = (status & 0xF);
#endif
    /* wait for RX */
    while (I2C0_PIRQSS.U == 0);
    if (I2C0_PIRQSS.B.RX == 0)
    {
        __debug();
        /* there is anything wrong, because RX must be set */
        /* clear the FIFO error(s) if exist */
        if (I2C0_ERRIRQSS.U) I2C0_ERRIRQSC.U = I2C0_ERRIRQSS.U;
        /* clear the error(s) and return */
        I2C0_PIRQSC.U = I2C0_PIRQSS.U;
        return ucBytesRead;
    }
    /* we clear the RX */
    I2C0_PIRQSC.B.RX = 1;
    /* we are now in MASTER RECEIVES BYTES state */
    while (count)
    {
        /* wait until any request will be raised */
        do {
            status = I2C0_RIS.U;
        } while (status == 0);
        /* check for any error */
        if (I2C0_ERRIRQSS.U)
        {
            __debug();
            /* clear the FIFO error(s) and return */
            I2C0_ERRIRQSC.U = I2C0_ERRIRQSS.U;
            /* TX_END is also set, we need to clear */
            I2C0_PIRQSC.B.TX_END = 1;
            return ucBytesRead;
        }
        if (status & ((1<<IFX_I2C_ICR_BREQ_INT_OFF) | (1<<IFX_I2C_ICR_LBREQ_INT_OFF)))
        {
            /* this is a burst request */
            /* dependent of the FIFO burst size setting we get only 1, 2 or 4 words */
#if RX_BURST_SIZE == 0
            for (ucCnt=0; ucCnt<1; ucCnt++)
#endif
#if RX_BURST_SIZE == 1
            for (ucCnt=0; ucCnt<2; ucCnt++)
#endif
#if RX_BURST_SIZE == 2
            for (ucCnt=0; ucCnt<4; ucCnt++)
#endif
            {
                /* get a value from FIFO */
                uiData = I2C0_RXD.U;
                /* save the data dependent of FIFO alignment */
                *pData++ = (uint8)uiData;
                uiData >>= 8;
                ucBytesRead++;
                count--;
#if RX_FIFO_ALIGNMENT != 2
                if (count)
                {
                    *pData++ = (uint8)uiData;
                    uiData >>= 8;
                    ucBytesRead++;
                    count--;
                }
#endif
#if RX_FIFO_ALIGNMENT == 0
                if (count)
                {
                    *pData++ = (uint8)uiData;
                    uiData >>= 8;
                    ucBytesRead++;
                    count--;
                }
                if (count)
                {
                    *pData++ = (uint8)uiData;
                    uiData >>= 8;
                    ucBytesRead++;
                    count--;
                }
#endif
            }
        }
        else
        {
            /* this is the a single access */
            /* get only 1 word from FIFO */
            uiData = I2C0_RXD.U;
            /* save the data dependent of FIFO alignment */
            *pData++ = (uint8)uiData;
            uiData >>= 8;
            ucBytesRead++;
            count--;
#if RX_FIFO_ALIGNMENT != 2
            if (count)
            {
                *pData++ = (uint8)uiData;
                uiData >>= 8;
                ucBytesRead++;
                count--;
            }
#endif
#if RX_FIFO_ALIGNMENT == 0
            if (count)
            {
                *pData++ = (uint8)uiData;
                uiData >>= 8;
                ucBytesRead++;
                count--;
            }
            if (count)
            {
                *pData++ = (uint8)uiData;
                uiData >>= 8;
                ucBytesRead++;
                count--;
            }
#endif
        }
        #if CR_BEHAVIOR == 0
        /* requests are not cleared automatically */
        /* we clear the request */
        I2C0_ICR.U = (status & 0xF);
        #endif
    }
    /* we wait until TX_END will be set */
    while (I2C0_PIRQSS.U == 0);
    /* TX_END is also set, we need to clear */
    I2C0_PIRQSC.B.TX_END = 1;
    /* if we are now in MASTER RESTART state, we generate a stop bit and going to LISTENING state */
    if (I2C0_ADDRCFG.B.SOPE == 0)
    {
        /* set */
        I2C0_ENDDCTRL.B.SETEND = 1;
        /* wait until TX_END is set */
        while (I2C0_PIRQSS.B.TX_END == 0);
        /* clear TX_END */
        I2C0_PIRQSC.B.TX_END = 1;
    }
    /* now we are in LISTENING state */
    return ucBytesRead;
}

0 Likes
userTC389
Level 1
Level 1
First reply posted First question asked Welcome!

Thank you for the quick reply.

Actually, i tried it in a way very similar to that because the code you pasted here seems very much organized like the sample code which doesn't really work for me.

As i wrote above, BREQ is ALWAYS set so RIS is always > 0. RX however is never set and that's where the implementation above fails to work. That's why i have asked the questions explicitely:
What do i need to do to reset BREQ?
Why is RX never set?
How do i manage RX to be set and the i2c logic to transfer only the amount of bytes specified in MRPS?

I'm afraid i can't really find any answers to these questions in your reply.

Thanks again and many greetings,

Oliver

 

0 Likes

Tried using the register accesses in the exact sequence as described in the sample code quoted above. Unfortuntaly, the device still behaves as i said: It uncontrollably transfers a full FIFO length despite MRPS set to "1" and then gets stuck in the final "while TX_END == 0" loop.

Please, what do i need to do to make it receive a limited number of bytes?

 

0 Likes
userTC389
Level 1
Level 1
First reply posted First question asked Welcome!

Copied the code from above into my project. Added all missing defines, changed the initialisation to be compliant to the FIFO-settings defined above.  I call "eeprom_read_bytes" for reading out 1 byte from address 2.

It transfers 2 bytes over i2c (checked SDA and SCL via oscilloscope), TPS and RPS are 0 when it runs into the "__debug()" call at "if (I2C0_PIRQSS.U != (0x1<<IFX_I2C_PIRQSS_TX_END_OFF))" which is fascinating because when the debugger halts at that specific "debug" instruction, only TX_END is set, meaning: The hardware must have had another flag set when the CPU read PIRQSC that is (immediatelly) being reset (by itself) afterwards.

But, in other words: The code quoted by MoD above in order to explain to me how the i2c interface should be operated to read a specific number of bytes doesn't work. At least not on the TC389QP i have on my desk.

Please, is there a way to control this interface properly in software or am i better off by re-activating my bit-banging driver?

Many greetings,

Oliver

 

 

0 Likes
MoD
Employee
Employee
50 likes received 500 replies posted 100 solutions authored

There is a small problem in the code, therefore you stops at the debug instruction, following lines:

    /* wait for Protocol or error interrupt */
    /* wait until any request will be raised */
    while (I2C0_RIS.U == 0);

 shoulde be change to this lines:

    /* wait for Protocol or error interrupt */
    while ((I2C0_RIS.U & 0x30) == 0);

On the original code, dependent of system and pipeline state, it is possible that the first read of I2C0_RIS will return a value which is not 0 because the transfer request flags are not yet cleared. Transfer request flags are not relevant here and can be ignored. With the corrected code the while loop will be left only if there is an error interrupt or protocol interrupt (e.g. TX_END only is set).

0 Likes
lock attach
Attachments are accessible only for community members.
MarkusNoll
Employee
Employee
25 replies posted 10 likes received 25 sign-ins

Dear Oliver,

I'm sorry that you had such a bad experience with our I2C-module.

I could recommend you to have a look into the reference-sources for the Aurix Application-Kits.

There are some I2C-samples inside for both read & write I actually used with no major issues in the past. Maybe that helps you. Is is based on the iLLD. 

 

Best,

Markus

0 Likes

Hello Markus,

thanks for the reply. I tried the files from iLLD before trying your code example and i couldn't get it to do what i wanted it to do either. It locks up because BREQ cannot be reset (as i wrote above) but the code in iLLD expects it to be "0" at a certain point.

Like i said, i got it to work as far as transmitting data goes and "can" receive bytes, even though the device always reads in as many bytes as it wants to, not as many as i want to. For the time being, i can live with that even though it is far from being a satisfactory, reliable and "well understood" driver (which i prefer).

I would appreciate an update in the AURIX2G user manual though because the chapter covering i2c is, unfortunately and untypical for Infineon, extremely confusing. I would also expect an errata sheet to cover at least some of the many issues i've experienced in my tests like, for example, the dysfunctional MRPS register, the missing RX-bit or the BREQ-irq that is permanently stuck.

Many thanks again, many greetings and have a nice weekend,

Oliver

 

0 Likes