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.

I2C Tiva's I2C Master Loopback

Hi,

I have been playing with my DK-TM4C129X board and wanted to try the I2C. I used the example provided in TivaWare peripheral folder. It seems it is a common issue in both stellaris and Tiva devices to get this example to work. I have done the following, which is almost identical to the posted example. However, It doesnt seem to receive any ack from the slave and gets stuck in the bolded line. Have there been any solutions to this issue yet?

int
main(void)
{
uint32_t pui32DataTx[NUM_I2C_DATA];
uint32_t pui32DataRx[NUM_I2C_DATA];
uint32_t ui32Index;

//
// Set the clocking to run directly from the external crystal/oscillator.
// TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
// crystal on your board.
//
ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN |
SYSCTL_USE_PLL |
SYSCTL_CFG_VCO_480), 120000000);

//
// The I2C6 peripheral must be enabled before use.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C6);

//
// For this example I2C6 is used with PortB[3:2]. The actual port and
// pins used may be different on your part, consult the data sheet for
// more information. GPIO port B needs to be enabled so these pins can
// be used.
// TODO: change this to whichever GPIO port you are using.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);


//
// Configure the pin muxing for I2C6 functions on port B2 and B3.
// This step is not necessary if your part does not support pin muxing.
// TODO: change this to select the port/pin you are using.
//
GPIOPinConfigure(GPIO_PB6_I2C6SCL);
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_6);

GPIOPinConfigure(GPIO_PB7_I2C6SDA);
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_7);
//
// Select the I2C function for these pins. This function will also
// configure the GPIO pins pins for I2C operation, setting them to
// open-drain operation with weak pull-ups. Consult the data sheet
// to see which functions are allocated per pin.
// TODO: change this to select the port/pin you are using.
//
// GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_6 | GPIO_PIN_7);

//
// Enable loopback mode. Loopback mode is a built in feature that is
// useful for debugging I2C operations. It internally connects the I2C
// master and slave terminals, which effectively let's you send data as
// a master and receive data as a slave.
// NOTE: For external I2C operation you will need to use external pullups
// that are stronger than the internal pullups. Refer to the datasheet for
// more information.
//
HWREG(I2C6_BASE + I2C_O_MCR) |= I2C_MCR_LPBK;

//
// Enable and initialize the I2C6 master module. Use the system clock for
// the I2C6 module. The last parameter sets the I2C data transfer rate.
// If false the data rate is set to 100kbps and if true the data rate will
// be set to 400kbps. For this example we will use a data rate of 100kbps.
//
I2CMasterInitExpClk(I2C6_BASE, ui32SysClock, false);
I2CMasterEnable(I2C6_BASE);

//
// Enable the I2C6 slave module. This module is enabled only for testing
// purposes. It does not need to be enabled for proper operation of the
// I2Cx master module.
//
I2CSlaveEnable(I2C6_BASE);

//
// Set the slave address to SLAVE_ADDRESS. In loopback mode, it's an
// arbitrary 7-bit number (set in a macro above) that is sent to the
// I2CMasterSlaveAddrSet function.
//
I2CSlaveInit(I2C6_BASE, SLAVE_ADDRESS);


I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, false);


InitConsole();


UARTprintf("I2C Loopback Example ->");
UARTprintf("\n Module = I2C6");
UARTprintf("\n Mode = Single Send/Receive");
UARTprintf("\n Rate = 100kbps\n\n");


pui32DataTx[0] = 'I';
pui32DataTx[1] = '2';
pui32DataTx[2] = 'C';

for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
pui32DataRx[ui32Index] = 0;
}


UARTprintf("Tranferring from: Master -> Slave\n");


for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
//
// Display the data that the I2C6 master is transferring.
//
UARTprintf(" Sending: '%c' . . . ", pui32DataTx[ui32Index]);


I2CMasterDataPut(I2C6_BASE, pui32DataTx[ui32Index]);


I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_SEND);


while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ))
{
}


pui32DataRx[ui32Index] = I2CSlaveDataGet(I2C6_BASE);


while(I2CMasterBusy(I2C6_BASE))
{
}

//
// Display the data that the slave has received.
//
UARTprintf("Received: '%c'\n", pui32DataRx[ui32Index]);
}

//
// Reset receive buffer.
//
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
pui32DataRx[ui32Index] = 0;
}

//
// Indicate the direction of the data.
//
UARTprintf("\n\nTranferring from: Slave -> Master\n");


I2CMasterSlaveAddrSet(I2C6_BASE, SLAVE_ADDRESS, true);

//
// Do a dummy receive to make sure you don't get junk on the first receive.
//
I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);


while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_TREQ))
{
}

for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
//
// Display the data that I2C6 slave module is transferring.
//
UARTprintf(" Sending: '%c' . . . ", pui32DataTx[ui32Index]);

//
// Place the data to be sent in the data register
//
I2CSlaveDataPut(I2C6_BASE, pui32DataTx[ui32Index]);

//
// Tell the master to read data.
//
I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);

//
// Wait until the slave is done sending data.
//
while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_TREQ))
{
}

//
// Read the data from the master.
//
pui32DataRx[ui32Index] = I2CMasterDataGet(I2C6_BASE);

//
// Display the data that the slave has received.
//
UARTprintf("Received: '%c'\n", pui32DataRx[ui32Index]);
}

//
// Tell the user that the test is done.
//
UARTprintf("\nDone.\n\n");

//
// Return no errors
//
return(0);
}

  • We've found that w/earlier M3 & LX4F M4 - the highlighted function must be called just post, I2CMasterControl().

    In addition - I see comments showing that I2C6 may have targeted both "Master & Slave" modes of operation - such may be possible - but outside my personal experience.  (devil often in such detail - believe it's far simpler to dedicate separate I2C instantiations for Master & Slave.  Compressing both into one I2C module may invite issues...)

    [edit] - Note "now" (but of course) that your Subject is "Master Loopback" - this forum nicely conceals the subject line - but still - my bad!  Loopback does have certain appeal - but as you report "shared difficulty" - the old standby of "known good" - independent I2C device, on remote board - escapes such complaint & often proves helpful during, "real/external world," test/verify...  Hallowed "Loopback" - perhaps not so much...

    // Display the data that the I2C6 master is transferring.

    UARTprintf(" Sending: '%c' . . . ", pui32DataTx[ui32Index]);

    I2CMasterDataPut(I2C6_BASE, pui32DataTx[ui32Index]);

    I2CMasterControl(I2C6_BASE, I2C_MASTER_CMD_SINGLE_SEND);

    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {} // Delay till xmsn completes

    while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ))
    {
    }

    This "fix attempt" is quick/easy & pastable - perhaps warrants your test...      

  • Hello cb1_mobile,

    Thanks for the answer but unfortunately, the suggested method doesn't seem to work as the program is still stuck in the loop. So, I assume the master is unable of sending the byte out, though I dun understand why. Any other suggestions:

    Quick note: I replaced your :

    while(ROM_I2CMasterBusy(I2C1_MASTER_BASE)) {}  with  while(ROM_I2CMasterBusy(I2C6_BASE)) {}

    as I2C6 is the one that I'm using.

    Thanx

  • Indeed - I "cut/pasted" w/out proper edit into your chosen I2C module.  My bad(2).

    You may benefit by opening & reviewing, "ROM_I2CMasterBusy()" especially looking to see if that function requires an "ACK" from the wired Slave.  Should that prove the case - your held hostage to that slave's processing & response.

    As always - KISS saves the day.  Have you a simple, small, I2C EEProm?  Suggest that you solve your issue via, "Divide & Conquer" - get the Master I2C peripheral up/running - only then would I move the "field of battle" to yonder Slave...

    Bon chance, mon ami. 

  • Hi Mil,

    I copied the test code you had pasted and got it working the first time around on TM4C129. However when I tried to restart the code in CCS, it gave me the same issue. A closer look at I2C Slave Register showed that it was waiting in Transmit State and hence it hangs on the Receive Request.

    A simple code fix to get it to work every time was to put SysCtlPeripheralReset(SYSCTL_PERIPH_I2C6) after the SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C6). After doing the above, it worked every time.

    I will look into why the Slave is in Transmit State.

    Hope that ungates you

    Regards

    Amit

  • Thank you very much @Amit Ashara and @cb1_mobile for the help. Koodos to @Amit Ashara for spot-on troubleshooting. 

     

  • cb1_mobile said:
    especially looking to see if that function requires an "ACK" from the wired Slave

    Indeed - benefits spring from: "insider/vendor info," that exact demo board, and that same IDE. 

    This reporter - minus any/all of the above - believes the (faster/earlier) diagnosis of, "failed ACK from the Slave" (per above) did not miss the mark...

  • Hi Mil,

    The issue is isolated to the slave. I would check how to update the code example for corrected sequence as part of the next TivaWare release.

    Regards

    Amit