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.

TMS570LS1227: Sample code for running I2C master transmitter mode with interrupt

Part Number: TMS570LS1227
Other Parts Discussed in Thread: HALCOGEN

Hi there,

I am looking for a sample code for transmitting to and receiving from a slave device based on the API generated by Halcogen.  Below is my first draft.  Note that this is assuming that the wiring and hardware are working as intended thus no error catching.

 

/* In high level driver */

IO_I2C_ret_E IO_I2C_transceive(const uint8_t * txBuffer,
                               const uint16_t txLength,
                               const uint8_t * rxBuffer,
                               const uint16_t rxLength)
{
    IO_I2C_ret_E ret = IO_I2C_RET_ERROR;
    
    /* Set direction to Transmitter */
    i2cSetDirection(i2cREG1, I2C_TRANSMITTER);
    
    /* Configure Data count */
    i2cSetCount(i2cREG1, (uint32_t) txLength);
    
    /* Configure address of Slave to talk to */
    i2cSetSlaveAdd(i2cREG1, Slave_Add);
    
    /* Start transmit*/
    IO_I2C_semaphoneTake();  // take semaphore to be returned by isr
    i2cSetStart(i2cREG1); // Start the transmission:
    
    /* send data */
    IO_I2C_semaphoneTake();  // take semaphore to be returned by isr
    i2cSend(i2cREG1, (uint32_t) txLength, txBuffer);

    if (rxLength > 0)
    {
        /* Set direction to Transmitter */
        i2cSetDirection(i2cREG1, I2C_RECEIVER);
        
        /* Configure Data count */
        i2cSetCount(i2cREG1, (uint32_t) rxLength); 
        
        /* Start transmit*/
        IO_I2C_semaphoneTake();  // take semaphore to be returned by isr
        i2cSetStart(i2cREG1); // Start the transmission:
        
        /* receive data */
        IO_I2C_semaphoneTake();  // take semaphore to be returned by isr
        i2cSend(i2cREG1, (uint32_t) rxLength, rxBuffer);
    }
    /* set stop */
    i2cSetStop(i2cREG1);

    ret = IO_I2C_RET_SUCCESS;

    return ret;
}

/* In Halcogen generated notification.c */

#pragma WEAK(i2cNotification)
void i2cNotification(i2cBASE_t *i2c, uint32 flags)      
{
/*  enter user code between the USER CODE BEGIN and USER CODE END. */
/* USER CODE BEGIN (21) */
    if( (i2cREG1 == i2c))
    {
        IO_I2C_semaphoreGive();
    }
/* USER CODE END */
}

If there is a reference code similar to this that I can refer to, that would be great.  Also below is the list of my questions:

Q1: In another thread in the forum, your Guru mentioned that i32cSetStart() will send #1 start condition, #2 slave device address, and #3 R/W bit.  Is it still true?

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1029269/tms570lc4357-can-provide-i2c-communication-steps-for-transmit-and-receive-as-master/3807094?tisearch=e2e-sitesearch&keymatch=sample%252525252520i2c%252525252520master#3807094

Q2: Does i32cSetStart() also operates in interrupt mode?  Meaning does the function returns immediately, and generates an interrupt when slave address is transmitted?

Q3: In i2cSend() and i2cReceive(), which I will refer them as the API functions, there is implementation for using interrupt.  In the interrupt implementation, it uses a static structure g_i2cTransfer_t to hold the data pointer and length.  However the API functions only assigns the initial value to the structure and exit, but I don't see any code that loop through it, nor the API functions being called in the interrupt notification.  Am I missing anything?  Or is there a DMA mechanism that automagically handles multi byte read / right, such that a call as below:

i2cSend(i2cREG1, 10, &data);

will result 1 call to i2cNotification() AFTER all 10 bytes are transmitted?

  • Also, in one of the GitHub link I found, it has a i2cInterrupt() implementation, which makes perfect sense to me.  However that function is not available in my Halcogen generated i2c.c.

    Github link: https://github.com/satellogic/canopus/blob/master/src/conf.d/halcogen/Torino1500/TMS570LS3137PGE/source/i2c.c

    Screen shot of my Halcogen.  Notice that I2C interrupt is in a weird place, and it is not clickable.  I am using Halcogen 04.07.01.

  • Notice that I2C interrupt is in a weird place, and it is not clickable.

    For HALCoGen to display properly under Windows 10, try setting the Display "Scale and layout" to 100%.

    See HALcoGEN Software seems to be bugged.

  • Screen shot of my Halcogen.  Notice that I2C interrupt is in a weird place, and it is not clickable.  I am using Halcogen 04.07.01.

    e2e.ti.com/.../3867770

  • Q1: In another thread in the forum, your Guru mentioned that i32cSetStart() will send #1 start condition, #2 slave device address, and #3 R/W bit.  Is it still true?

    Yes

  • Q2: Does i32cSetStart() also operates in interrupt mode?  Meaning does the function returns immediately, and generates an interrupt when slave address is transmitted?

    No, the AAS interrupt is only generated when the I2C has recognized its own slave address.

  • QJ thanks for your reply.  Let me clarify my question, as I am not asking about the AAS Interrupt.  According to your response to my Q1, when I call i2cSetSrart(), it is going to transmit the START bit, slave address, and R/W bit.  It appears that i2cSetSrart() will return immediately.  Do I have to wait for Tx Ready interrupt after i2cSetSrart() before calling i2cSend()?

  • Chester thanks for your reply.  I am running Parallel desktop on Mac (virtual machine with windows 10), and it is a bug that never get fixed.  Thus let’s don’t focus on fixing the gui.

    looking at the content of i2cInterrupt() in my GitHub link, I think having the function is critical to make my code work.  Does Halcogen generate the function?  If Halcogen is capable of generating the function, but it is not generating it for me because I couldn’t click on the gui, can you point to me which flag in the dil file i can modify in order to make it work?

  •  If Halcogen is capable of generating the function, but it is not generating it for me because I couldn’t click on the gui, can you point to me which flag in the dil file i can modify in order to make it work?

    With HALCoGen v04.07.01 running on Windows 10 changed the I2C options in the GUI for a TMS570LS1227PGE to see what options in the .dil file affected the generated code.

    The GUI option which controls if the i2cInterrupt() function is generated is Interrupt Assignment "66 : IC2 Interrupt" on the VIM Channel 64-95 tab:

    The i2cInterrupt() function is generated when the .dil file contains:

    DRIVER.SYSTEM.VAR.VIM_CHANNEL_66_INT_ENABLE.VALUE=1

    The Interrupts on the I2C driver tab control the value written to the i2cREG1->IMR interrupt enable register by the generated i2cInit() function:

    Where the mapping of the enabled IC2 interrupts to the .dil file parameters is:

    Interrupt name on HALCoGen GUI .dil file parameter name which is set to 1 when interrupt enabled in GUI
    AL INT DRIVER.I2C.VAR.I2C_ALINTENA.VALUE
    NACK INT DRIVER.I2C.VAR.I2C_NACKINTENA.VALUE
    ARDY INT DRIVER.I2C.VAR.I2C_ARDYINTENA.VALUE
    ICRRDY DRIVER.I2C.VAR.I2C_ICRRDYINTENA.VALUE
    ICXRDY DRIVER.I2C.VAR.I2C_ICXRDYINTENA.VALUE
    SCD INT DRIVER.I2C.VAR.I2C_SCD.VALUE
    AAS INT DRIVER.I2C.VAR.I2C_AAS.VALUE
  • Thank you Chester.  I am able to enable I2C Interrupt IRQ Channel 66 through Halcogen GUI, and see the necessary changes including i2cInterrupt() function being generated.  I believe this resolved my issue along with CJ's reply.  I will confirm if the issue has been resolved in the next two days.

  • Hi Chuo,

    Please let us know or close the thread if the issue has been resolved. Thanks

  • Hi QJ, I am having problem verifying the interrupt method due to faulty hardware.  However this brings up another question.  I configure the MCU as I2C Master Transmitter.  In the case I have a faulty I2C slave device that is unable to ACK, it seems that I am not getting the NACK interrupt.  Note that I enabled the VIM channel 66 for I2C and NACK INT.  Do you know if I supposed to get a NACK interrupt right after start condition, slave address, and R/W bit?

  • Hi Chuo,

    I2C Protocol starts with a start bit followed by the slave address (7 bit address + 1 bit for Read/Write). After sending the slave address, Master releases the data bus(SDA line), put the line in high impedance state leaving it for slave to drive the line.

    If address matches the slave address, slave pull the line low for the ACK. If the line is not pull low by any of the slave, then Master consider it as NACK and sends the Stop bit or repeated start bit in next clock pulse to terminate the or restart the communication.

    External pull-up is required for I2C SDA and SCK lines. The MCU internal pull-up is too weak for I2C. 

  • BTW, NACK interrupt is not generated by no-acknowledgement after Start byte. In master start byte mode, the first byte (address of all zeroes) receives a NACK but does not clear the stop bit.

  • Hi QJ,

    I received the reworked board and confirmed I can transmit in polling mode.  However I couldn't get the interrupt mode to work.  Consider the following code:

                uint8_t txLength = 2;
                uint8_t txBuffer[2] = {0x00,0x11};
                
                /* Configure address of Slave to talk to */
                i2cSetSlaveAdd(i2cREG1, SLAVE_DEVICE_ADDRESS);
                /* Set direction to Transmitter */
                i2cSetDirection(i2cREG1, (uint32_t) I2C_TRANSMITTER);
                /* Configure Data count */
                i2cSetCount(i2cREG1, (uint32_t) txLength);
                /* Set mode as Master */
                i2cSetMode(i2cREG1, I2C_MASTER);
                /* set stop */
                i2cSetStop(i2cREG1);
                /* Start transmit*/
                i2cSetStart(i2cREG1); // Start the transmission:
                /* send data */
                i2cSend(i2cREG1, (uint32_t) txLength, txBuffer);

    Device is configured as I2C MASTER Transmitter, with repeat mode enabled, and Interrupt for RX, TX, NACK and SCD.  Also VIM channel 66 for I2CInterrupt is enabled.  It supposed to transmit two bytes of data.  However, the master only transmit the slave address/RW byte and one data byte (0x00) before issuing the stop condition.  It also appears that interrupt was never triggered.  Do you have any idea?

  • Which i2C interrupt are enabled? Did you enable IRQ interrupt? After reset or power up, the IRQ and FIQ are disabled by default.

    _enable_IRQ(); --> enable IRQ interrupt

  • Hi QJ,

    I just want to update that, along with further digging into the reference doc and disabling Repeated Start, I got it working for both TX and RX.  I do want to suggest you guys to add more detail in section: 

    31.2.5.3 Using the Repeated START Condition

    [suggested addition] In Repeat mode, STOP condition will NOT be generated automatically when TX/RX byte count reaches I2CCNT.  The user has to manually set STOP condition.  Refer to 31.6.10 I2C Mode Register (I2CMDR) for more detail.

    The reason is that in Repeat mode, STOP condition won't be generated automatically by HW, thus looking for SCD condition is not going to yield anything.  This goes to polling method too.

    Attached is the working Halcogen setup:

    Stripped down Code:

    
    bool IO_I2C_transceive(const uint8_t deviceAddr,
                           uint8_t * txBuffer,
                           const uint16_t txLength,
                           uint8_t * rxBuffer,
                           const uint16_t rxLength)
    {
        bool ret = false;
        
        // Obtain transmission line semaphore.  Timeout in 10ms
        ret = SEMAPHORE_TAKE(10);
    
        if (ret)
        {
            //
            // Transmit START condition, slave device addr, and R/W bit (read)
            //
            /* clear Previous I2C stop condition */
            i2cClearSCD(i2cREG1);
            /* Configure address of Slave to talk to */
            i2cSetSlaveAdd(i2cREG1, deviceAddr);
            /* Set direction to Transmitter */
            i2cSetDirection(i2cREG1, (uint32_t) I2C_TRANSMITTER);
            /* Configure Data count */
            i2cSetCount(i2cREG1, (uint32_t) txLength);
            /* Set mode as Master */
            i2cSetMode(i2cREG1, I2C_MASTER);
            /* set stop */
            i2cSetStop(i2cREG1);
            /* Start transmit*/
            i2cSetStart(i2cREG1); // Start the transmission:
            /* send data */
            i2cSend(i2cREG1, (uint32_t) txLength, txBuffer);
            
            ret = SEMAPHORE_TAKE(1); // timeout in 1ms
            /* Clear the Stop condition */
            i2cClearSCD(i2cREG1);
        }
    
        if (ret)
        {
            //
            // Transmit START condition, slave device addr, and R/W bit (read)
            //
    
            /* Configure address of Slave to talk to */
            i2cSetSlaveAdd(i2cREG1, deviceAddr);
            /* Set direction to Transmitter */
            i2cSetDirection(i2cREG1, (uint32_t) I2C_RECEIVER);
            /* Configure Data count */
            i2cSetCount(i2cREG1, (uint32_t) rxLength); 
            /* Set mode as Master */
            i2cSetMode(i2cREG1, I2C_MASTER);
            /* set stop */
            i2cSetStop(i2cREG1);
            /* Start transmit*/
            i2cSetStart(i2cREG1); // Start the transmission:
            /* receive data */
            i2cReceive(i2cREG1, (uint32_t) rxLength, rxBuffer);
            
            ret = SEMAPHORE_TAKE(1); // timeout in 1ms
            /* Clear the Stop condition */
            i2cClearSCD(i2cREG1);
        }
        // return transmission line semaphore
        SEMAPHORE_GIVE();
    
        return ret;
    }
    
    /* In notification.c */
    void i2cNotification(i2cBASE_t *i2c, uint32 flags)
    {
        SEMAPHORE_GIVE_FROM_ISR();
    }