Closed jasonmhite closed 5 years ago
I never needed it, so never considered it nor even looked at it :-). The application note seems quite easy to implement, except some potential conflict with dual I2C+GPIO usage [AD5 being right in the middle of the free GPIO pins] As often, the main issue here is to test a new feature, and have enough time to run non-regression tests. I do not think I have an I2C slave at hand which implements clock stretching. It could be simulated - I just lack spare time, as usual...
Thanks about the nice comment about this library!
I can help test if you like, at least with the BNO055 as it's the only device I have that uses it. Unfortunately I don't think I understand the FT232 well enough to really implement it myself or I'd try.
Actually, if you could even just provide a prototype implementation, I can probably get it working the rest of the way from there.
See i2c-clock-stretching branch.
Note that AN411 Application Note mentions AD5 as the MPSSE adaptive clock input pin, while FT2232H mentions AD7 for this. I did not get time to track down which one is right, so you better try all of them :-)
This change will also collide with yet-to-be-committed changes, BTW.
i.e. c2e4fd0ba
You are my hero. I will work on this when I get home next week. If I can get it working well then we can talk about cleaning it up to go with whatever other commits you have to merge back, assuming you are interested.
Also wow, that's simpler than I expected. I was thinking more setup was required. Now just a little luck to see if it works or not.
Alright, so I finally had a chance to hook it up and test.
It does work. It's not completely flawless, though. I have a simple test to get the chip id along the lines of:
import pyftdi as F
from pyftdi import i2c
import atexit
ctrl = i2c.I2cController()
ctrl.configure("ftdi://ftdi:232h/1", clockstretching=True)
atexit.register(ctrl.terminate)
port = ctrl.get_port(0x28)
client_id = port.read_from(0x00, readlen=1)
print(hex(client_id[0])) # Should be 0xA0
Without clock stretching it doesn't work, this gives an NACK because the slave doesn't answer immediately, as expected. Enabling the clock stretching / adaptive clock setting in your patch, it works perfectly the first time after I unplug and plug in the FT232H board. If I subsequently re-run without power cycling the FT232 it's a bit flaky... sometimes I get NACKs, sometimes I get the correct value, and sometimes I get a different hex value (seems random). I can rerun it over and over again and get different values every time, and then if I power cycle the board it works again on the first run.
This suggests to me that the adapter is somehow not finishing some of the transactions and getting stuck. I suspect it's related to the caveat mentioned in the application note:
MPSSE commands which involve waiting for a line state may wait indefinitely if that state does not occur for some reason and may require an MPSSE reset (SetBitMode to 0 and then SetBitMode back to MPSSE mode followed by initialising the MPSSE settings) to return to normal operation.
So I think perhaps there needs to be some timeout logic. I need to investigate further with a logic analyzer to confirm what's going on, but that's where I am at the moment.
PS: On my FT232H board, it's AD7
for the clock sense line. AD5
does not work at all, the adapter fails to respond. I also have an FT4232H board, but I haven't tried that yet.
Ok good news! Thanks for the feedback.
You could try the FTDI hack to go back and forth from MPSSE and RESET this way:
diff --git a/pyftdi/ftdi.py b/pyftdi/ftdi.py
index 6dc555d..90537c3 100644
--- a/pyftdi/ftdi.py
+++ b/pyftdi/ftdi.py
@@ -526,6 +526,8 @@ class Ftdi:
# Set chunk size
self.write_data_set_chunksize(512)
self.read_data_set_chunksize(512)
+ # Reset feature mode
+ self.set_bitmode(0, Ftdi.BITMODE_RESET)
# Drain buffers
self.purge_buffers()
# Disable event and error characters
if it does not help, try to shake it :-)
index 6dc555d..15f20e3 100644
--- a/pyftdi/ftdi.py
+++ b/pyftdi/ftdi.py
@@ -526,6 +526,12 @@ class Ftdi:
# Set chunk size
self.write_data_set_chunksize(512)
self.read_data_set_chunksize(512)
+ # Reset feature mode
+ self.set_bitmode(0, Ftdi.BITMODE_RESET)
+ # Enable MPSSE mode
+ self.set_bitmode(direction, Ftdi.BITMODE_MPSSE)
+ # Reset feature mode
+ self.set_bitmode(0, Ftdi.BITMODE_RESET)
# Drain buffers
self.purge_buffers()
# Disable event and error characters
Alright, another update. I think the flaky behavior is just a problem with this chip being really kind of awful and requiring arbitrary delays between certain operations and not a problem with the clock stretching.
So, with those added the clock stretching appears to be working and I can actually get orientation data out of the sensor! More testing required, however. There's still some glitches and I need to make sure they are the fault of the chip not the clock stretching you added (I'm pretty sure it's the chip being crap, but I need to test).
Also regarding the changes for clock stretching conflicting with unmerged changes, what if instead of making it a flag to I2cController
, it was put in a subclass? The behavior is different enough that it makes sense to me to separate it from the basic i2c implementation, especially because it's not really a supported feature of the chip and it might conflict with other functionality.
I think I could probably pull out your changes into a subclass that reuses the constructor from I2cController
and then just turns on the adaptive clock. No conflicts, and the subclass won't actually do very much so it doesn't add much to maintain.
Here's an example of what I am thinking:
from pyftdi.i2c import I2cController
class I2cControllerClockStretch(I2cController):
def configure(self, url, **kwargs):
super().configure(url, **kwargs)
self._ftdi.enable_adaptive_clock(True)
That is working for me at the moment. If you're not interested in merging clock stretching support I can live with that since it's easy to layer on top of pyftdi, but I think it's pretty useful and I don't know of any other implementations that support clock stretching.
I'm not a big fan of implementation subclassing - and I think in this case it would be a nice trap to fall in on each upgrade. The problem here is that it creates a "hole" in the available GPIO pins for I2C+GPIO usage.
I do not think it hurts to add clock stretching option to the main class.
If the current implementation works for you, I'll try to add this feature to the main branch at some point next month - on vacation till the end of the year.
jasonmhite or eblot,
Could one of you share a complete example of a working clock stretching python code under linux? Even if it is a hack. I too want to use a bno055 using i2c from a usb connection. I prefer this over using the serial configuration (which i have done before), because now I have other i2c devices i need to use in the same application and want to be neat with my wiring. Thanks so much if you can help!
@aprovecharLab Use the branch eblot posted earlier and pass clockstretching=True
in the configure
method of I2cController
. You also need to connect a line to the clock bus for sensing, on my board it's AD7
.
That was it, thanks! it was the ad7 pin rather than ad5 for me as well ... i'm using the adafruit FT232H board.
Steve
On Sat, Feb 23, 2019, at 7:39 PM, Jason Hite wrote:
@aprovecharLab https://github.com/aprovecharLab Use the branch eblot posted earlier and pass
clockstretching=True
in theconfigure
method ofI2cController
. You also need to connect a line to the clock bus for sensing, on my board it'sAD7
.— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eblot/pyftdi/issues/111#issuecomment-466718920, or mute the thread https://github.com/notifications/unsubscribe-auth/Arbkgy96JRzS4tbxp8iHyfYBZR8U9oprks5vQd9QgaJpZM4Ypv8d.
-- Stephen Brown AprovecharLab L3C srbrown@aprovechar.org
@aprovecharLab PS I never really got the BNO055 working well, though I don't think it has anything to do with the clock stretching. I could interact with it and read data just fine, but the self-calibration routine didn't seem to work properly and it always would raise error flags. I don't think it had anything to do with pyftdi (I've found a few discussions suggesting the chip is just buggy), but if you notice anything similar it might be worth investigating to see if it does.
Note that clock stretching is available in the master branch.
I have not closed this ticket as I was waiting for feedback from @jasonmhite
No feedback, closing.
I'm looking to use the FT232H with the Bosch BNO055 I2c orientation sensor, which requires clock stretching to operate. #91 mentions that clock stretching is not currently supported and as I understand it, this is because the FT232 drives the clock line to zero.
The FTDI application note seems to imply that clock stretching is possible using the adaptive clocking feature (section 8.2). Could support for this be added to pyftdi? As far as I know, none of the other FT232 libraries support this at the moment.
Also, thank you for making this library. Either way it's still by far the best implementation I've used.