Hi everyone,
Hi Tim,
Thanks for your example. Your idea of including a timeout helped me sort out the whole thing, and I think I have now a clear idea of what is happening and how to proceed. There are 3 things to take care about:
1- The main culprit is, as I suspected in my previous post, Ameba Low Level API’s I2C_SlaveWrite()
function. This function fails to test the RX_DONE
flag, hence cannot return upon the closing NACK.
I reimplemented the function and now use my own version. I just had to change the final
while((I2C_CheckFlagState(I2Cx, BIT_IC_STATUS_TFE)) == 0);
for
while(((I2C_CheckFlagState(I2Cx, BIT_IC_STATUS_TFE)) == 0) &&
((I2Cx->IC_RAW_INTR_STAT & BIT_IC_RAW_INTR_STAT_RX_DONE) == 0));
Now i2c_slave_write()
does not block forever any more and returns correctly – even if the I2C master reads fewer bytes than what I sent to the Tx FIFO.
This is also the place where I included the timeout as per your idea, although I have to say that once I did things cleverly, it never triggered once. I just keep it as a precaution. Here is the version of the lines above including the timeout:
int count = 0;
while(((I2C_CheckFlagState(I2Cx, BIT_IC_STATUS_TFE)) == 0) &&
((I2Cx->IC_RAW_INTR_STAT & BIT_IC_RAW_INTR_STAT_RX_DONE) == 0) &&
(count++ < MAXCOUNT));
You have to use a logic analyzer to check the speed of this loop and determine a proper value for MAXCOUNT. On my setup every loop accounted for about 2µs (including the fact that I actually had to toggle a GPIO line inside the loop to give a signal to my analyzer), but your mileage may vary.
2- Once i2c_slave_write()
returns, typically the slave software is still in a ReadAddressed
switch case. Before breaking, you absolutely want to cleanup the status of the I2C device in order to allow the software to switch to the NoData
case the next time. I tested two methods to do this:
I2C_Cmd(I2Cx, DISABLE);
I2C_Cmd(I2Cx, ENABLE);
which clears the Tx and Rx FIFOs, but leaves the interrupt flags untouched;
and
I2C_ClearAllINT(I2Cx);
which just clears all interrupts.
Both methods give equally satisfactory results, although not performing the same operation. I don’t know why. Failing to use either of them keeps the system in the ReadAddressed
state forever (which you described earlier in this thread). Should the master attempt to perform subsequent Write transactions, the slave would not be able to switch to WriteAddressed
to read the received data. Eventually, once the 16-byte Rx FIFO is full, the I2C bus may hang.
3- You want to make sure that
switch (slave_receive((i2c_t *)device))
is called often enough. Here the key parameter is the I2C master’s I2C timeout. Let T
be the period at which you call slave_receive()
. Upon a Read Request by the master, the RTL8720 performs an I2C clock stretch which lasts until the slave software actually sends bytes to the Tx FIFO. This clock stretch may last as long as T
. Should the master’s I2C timeout elapse before T, what happens is that the master merely relinquishes the bus and abandons the transaction. Then, when the RTL8720 eventually has something to transmit, there is no clock any more on the bus to empty the Tx FIFO and complete the transaction.
Hence T
should always be made shorter than the master’s I2C timeout by a good margin.
Now my system runs as smoothly as can be. Too bad the Ameba documentation is so sparse about how to use the I2C slave API successfully in the first place.
Hope that helps !
Xavier