Hi,
I'm using the I2C Loopback example to teach myself how to operate the I2C module. So that I can in turn teach it to my students. I'm taking the loopback code straight from the example and converting it line by line to use direct register access instead of peripheral driver library functions. But I believe I've come across an error in the code. Just for the record I'm using the Tiva C Launchpad (tm4c123gh6pm), TivaWare 2.1.0.12573, and CCS 6.0.0.00190. The part that has an issue is when the master is requesting to receive data from the slave. The original (problematic) TivaWare code:
//
// 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");
//
// Modifiy the data direction to true, so that seeing the address will
// indicate that the I2C Master is initiating a read from the slave.
//
I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
//
// Do a dummy receive to make sure you don't get junk on the first receive.
//
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
//
// Dummy acknowledge and wait for the receive request from the master.
// This is done to clear any flags that should not be set.
//
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
{
}
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
//
// Display the data that I2C0 slave module is transferring.
//
UARTprintf(" Sending: '%c' . . . ", pui32DataTx[ui32Index]);
//
// Place the data to be sent in the data register
//
I2CSlaveDataPut(I2C0_BASE, pui32DataTx[ui32Index]);
//
// Tell the master to read data.
//
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
//
// Wait until the slave is done sending data.
//
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
{
}
//
// Read the data from the master.
//
pui32DataRx[ui32Index] = I2CMasterDataGet(I2C0_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);
}
I don't think that "Dummy Receive" stuff before the for loop does what it says it does. What it seems to do is queue up a receive. That while loop makes it so that as soon as the slave has acknowledged that the master is requesting data it goes into the for loop. During that first UART printf in the for loop the master is already waiting for data. The slave puts data into its data register which triggers the transmit back to the master. The Master receives it and then queues up the next receive. This program "works" in that the 3 bytes of data get sent and received correctly but the problem is that after the third receive the master has queued up a fourth so when "Done" is printed the slave's TREQ (transmit requested) flag is '1'. So if you wanted to add on to the end of this program the master would already be waiting for the slave to send more data.
The comments attached to the code seem to spell out that the entire transfer of a single character should take place during a single iteration of that for loop. Either that "dummy receive" functionality is unnecessary or improperly implemented. Here's what my code looks like now (there is a mix of peripheral driver functions and direct register access as I'm still working on it, sorry if that's confusing):
//
// 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");
//
// Modifiy the data direction to true, so that seeing the address will
// indicate that the I2C Master is initiating a read from the slave.
//
//I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
I2C0_MSA_R |= I2C_MSA_RS;
//
// Do a dummy receive to make sure you don't get junk on the first receive.
//
//I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
//I2C0_MCS_R = I2C_MCS_START|I2C_MCS_STOP|I2C_MCS_RUN;
//
// Dummy acknowledge and wait for the receive request from the master.
// This is done to clear any flags that should not be set.
//
//while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
//{
//}
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
//
// Display the data that I2C0 slave module is transferring.
//
UARTprintf(" Sending: '%c' . . . ", pui32DataTx[ui32Index]);
//
// Tell the master to read data.
//
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
//
// Wait until the slave acknowledges it needs to transfer.
//
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
{
}
//
// Place the data to be sent in the data register
//
I2CSlaveDataPut(I2C0_BASE, pui32DataTx[ui32Index]);
//
// Wait until module is done transferring.
//
//while(I2CMasterBusy(I2C0_BASE))
while(I2C0_MCS_R & I2C_MCS_BUSBSY)
{
}
//
// Read the data from the master.
//
pui32DataRx[ui32Index] = I2CMasterDataGet(I2C0_BASE);
//
// Display the data that the master 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);
}
My version gets rid of the "dummy receive" stuff all together (it's commented out so you can see where it was). It successfully transfers the 3 bytes. During the first printf in the for loop the master isn't already waiting for data and during the final printf the I2C module is actually Idle as opposed to having the master waiting for another transfer.
It seems to me like this is how the I2C control flow is "supposed" to work and that the TivaWare should be updated to something similar to this. If the dummy stuff needs to be re-added for whatever reason it should be implemented differently.
If I'm seeing things incorrectly or am misunderstanding anything please explain where I went wrong. If you need a complete version of my code let me know. (I tried to only post the pertinent pieces, given that its a standard TivaWare example). Thanks.