Just found my notes from a while ago. Let me check if I can share anything.
For example, this is the way I created the ‘workaround’ for the slave getting stuck on the interrupt. I won’t win any contest with this code, since it’s not nice designed. However I couldn’t find a solution within the SDK which solved my problem.
void I2C_Client::handlePollingFromMaster()
{
switch (slave_receive(((i2c_t *)m_pI2C)))
{
case ReadAddressed:
{
if (m_readTimeout > MAX_READ_TIMEOUT)
{
if (I2C_GetRawINT(((i2c_t *)m_pI2C)->I2Cx) & BIT_IC_RAW_INTR_STAT_RD_REQ)
{
I2C_ClearAllINT(((i2c_t *)m_pI2C)->I2Cx);
}
}
m_readTimeout++;
break;
}
case WriteAddressed:
{
char buff[1];
if (i2c_slave_read(((i2c_t *)m_pI2C), buff, 1) > 0)
{
// Do your operation based on the Byte which is sent by the Master.
}
break;
}
case WriteGeneral:
{
break;
}
default:
{
break;
}
}
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
Hi Simon,
You’re welcome. To further refine my point, I think that the device cleanup I described in my 2- above should actually be performed inside the I2C_SlaveWrite()
function right at the end, because anyway this action must be performed every time I2C_SlaveWrite()
returns.
However I am not sure which is the appropriate cleanup action to perform. Is it a device DISABLE
+ ENABLE
? Is it an I2C_ClearAllINT()
? Is it just an I2C_ClearINT(RD_REQ)
? Probably only Realtek people aware of the detailed underlying mechanism could make a wise decision.
Anyway I think the I2C_SlaveWrite()
function deserves an update in the next Ameba SDK release. This would make the life of I2C slave software developers considerably easier. Do you know what is the best way to request such an update?
Xavier
Dear @xsutter
This part is handled by other developing team thus it will be updated only if they deem necessary.
Thanks for your feedback though, we will feedback to them once we have the chance
Hi @xidameng ,
This question appears to have been settled some time ago already: see
I2C Slave API fails to recognize the master’s final NACK in slave-to-master transmissions.
Unfortunately the correction to the AmebaD SDK was not propagated to the Arduino SDK, which appears to still contain the buggy I2C_SlaveWrite()
. I will have to file an issue for this as well.
Cheers,
Xavier
Thanks for your contribution, we will review the PR and merge it once review is completed
Hi @xsutter
Can I check if you are still using the Arduino Wire lib (ameba edition) for the slave tx and rx or have switch completely to using the APIs available in i2c_api.c
and rtl8721d_i2c.c
?
Thanks!
Simon
Hi Simon,
Nice to read you !
I do not use the Arduino Wire lib at all, since the Wire.onReceive
and Wire.onRequest
callbacks never get called. I do not even #include "Wire.h"
. I just #include "i2c_api.h"
and use the Mbed and raw APIs, to my complete satisfaction.
As you already know, the current release of the Arduino SDK still contains a buggy I2C_SlaveWrite()
. I filed an issue on this. In the meantime I am using a private, corrected version of I2C_SlaveWrite()
which now resides permanently in my code.
Cheers,
Xavier
Nice to hear you back and good to know that the I2C works fine in your setting.
The reason why I asked is because we are planning to support the
in the Wire
libraries since it has been requested many times and I am wondering if you would like to test it once it’s done so we can confirm that I2C slave works in the interrupt mode under user’s code?
Yes, I can adapt my application to serve as a test support, this should not take too much time. Tell me when the new Wire is ready!
However unless we observe a definite performance advantage in using the Wire library, we will likely stick with the underlying Mbed API in our final product. This is in the hope that a VSCode Ameba SDK will eventually emerge, which would allow us to unify our development environment and get rid of the uncomfortable Arduino toolset completely.
Thanks! it shoud not take long~
So your dev env has been with VSCode? As far as I know, Ameba SDK usually stick to either GCC-Make/CMake env (which can be embedded into any IDE actually) or IAR env.
If VSCode is what you prefer, maybe a simple extension that integrate some core make command is all it takes.
Actually our project is built around 2 processors: ESP32 for the main application, plus RTL8720 BW16 as a 5GHz wifi peripheral. We use SPI + I2C to link both.
We use VSCode + PlatformIO + esp-idf for our our main development on ESP32, which is extremely comfortable. We wish we could do the same for RTL8720, but unfortunately at the time being we have no choice but use the Arduino environment, which is prehistoric in comparison. We do not have the resource to attempt porting the Ameba SDK to PlatformIO, nor to adapt PIO’s upload tool to the BW16. We just hope someone will do that some day…
PlatformIO has been requested many times too and it has been discussed internally and externally with the PlatformIO team. However, the PlatformIO team seem reluctant to help porting the Ameba SDK to their build system so this has to be put on pending for now.
Hello, @xidameng .
I was wondering on the status of this development? I noticed the latest commit for ambd_arduino/Arduino_package/hardware/libraries/Wire/examples/SlaveWriter/SlaveWriter.ino
was Jul 28 which leads me to believe this is the updated version. However, using this example has not provided the expected function when using the corresponding Master program.
Best,
Luke
Hi @luke I2C Slave has been implemented and added to Arduino SDK since last release [Release] Ameba Arduino SDK Official Release about 8 days ago.
If you run the I2C slave example provided with another I2C master, you should see I2C slave device sending/receiving data
could you elaborate on your settings?
You can refer to this example guide for detailed steps and demo
Apologies for resurrecting the thread, it was as simple as updating through Arduino’s Board Manager. I was still working under version 3.1.3 and now using 3.1.4 and it works fine.
Thank you for your timely response. I appreciate you and this communication medium setup between users and developers.
No problem luke, happy coding~