pyftdi icon indicating copy to clipboard operation
pyftdi copied to clipboard

exhange command results in shifted data

Open EveOnlinePlayer opened this issue 5 years ago • 7 comments

about 34% of the time, the address read byte doesn't shift out the last clock from the C232HM-EDHSL-0 MPSSE cable, until the start of the data byte, which makes the data reported to python appear shifted right one clock. So I'm reading a 0x3A, and 30-45% of the time I get a 0x1D I've been testing my code with a known device with a known response. It does not seem to matter if I use the relax or start options in any configuration. Tested on multiple cables, and after a reboot.

Results of doing an 'exchange' single-transaction write-read or a discrete write then a read from the same device: badE is: 3424 (exchange) badWR is: 0 (write command then read command) of 10,000 runs 514.4302661418915 (seconds) Done

my code: import time import binascii from pyftdi.i2c import I2cController i2c=I2cController() i2c.configure('ftdi://ftdi:232h:FT0NCMMI/1')

slave = i2c.get_port(0x4C) t=time.time() badE=0 badWR=0 runs=10000

for x in range(0, runs): firmwareID = slave.exchange([0xD0], 1, relax='false', start='true') if int.from_bytes(firmwareID,byteorder='big')!=58 : badE+=1 slave.write([0xD0],relax='true', start='false') firmwareIDRead = slave.read(1,relax='true', start='false') if int.from_bytes(firmwareIDRead,byteorder='big')!=58 : badWR+=1

print('badE is:',badE) print('badWR is:',badWR) print('of ',runs,' runs') print(time.time()-t) print('done')

This is an exchange from command: slave.exchange([0xD0], 1) exchange_4CW_D0_4CR_3AData_bad

Note the last clock on the address read: Bad_Addr_R01 It looks to me as if the address[R] ack bit from the slave is not being clocked in.

versus this "good one": Good_Addr_R01

and here is the exchange version of the data read: Bad01 looks like the address[R] ACK is clocked right before the data byte is fetched, but counted as a clock for the data byte.

versus the read version of the data read (good one): Good01

interesting that the scope (and I haven't checked yet, but I assume the slave part itself) always thinks this is OK. it's just the data into python that is shifted right one bit.

for now i'll use the write and then read commands as they work all the time (so far).

EveOnlinePlayer avatar Aug 05 '20 19:08 EveOnlinePlayer

So a coworker (who is infinity better at this) ran the same code on their machine with no issues. The hunt is on for what is different.

EveOnlinePlayer avatar Aug 10 '20 14:08 EveOnlinePlayer

Maybe a dup of #205.

Sorry, PyFtdi is on stall at the moment, I really have no spare time to look at those issues, but I'm pretty sure there is a bug.

eblot avatar Aug 10 '20 15:08 eblot

I changed my pull-up values and "it's working perfectly now" although there's hardly any difference on the scope clock and data edges (the ack clock is no longer mis-positioned). My co-worker was using 1k pull-ups on the 3.3V FTDI, I was using 4.6k on the 5V FTDI. I've changed to 7.16k and it works now.
I'll come back to this once my bench is running and see if there is some small timing issue causing the concern. I agree it is probably related to 205, though 205 deals with the stop, this is probably about timing more generally.

EveOnlinePlayer avatar Aug 10 '20 17:08 EveOnlinePlayer

Hi, Follow up for folks having trouble with clock stretching. C232HM-EDHSL-0 MPSSE cable Blue wire "GPIO3" is schematically inside the dongle the "GPIO7"/ADBUS7 pin. Use a diode from slave clock (anode) to master clock (cathode) on the orange wire (master). Connect the blue wire to slave clock (anode of diode). Green and yellow together for SDA.

i2c = I2cController() i2c.configure('ftdi://ftdi:232h:FT0J75U1/1', frequency=100000, clockstretching=True)

image Note the CH2 is on the Orange master clock out, SCL_M. CH3 is the Slave Clock (anode side), SCL_S CH4 is SDA. Note how this slave gets a ADDR[R] and stops the clock prior to the ACK bit. Any I2C Slave can stop or slow the clock anytime.. so that isn't a problem. This slave needed an extra ~60us to fetch the data since the end of the prior write command. Note how CH3 is low(er) pulled down by the slave until it is ready to clock out the data in the buffer.

Happy I2Cing.

P.s. eblot, your library is EPIC. Well written, easy to use, and above all FAST!!

EveOnlinePlayer avatar Aug 17 '20 17:08 EveOnlinePlayer

Sorry, not very active on this project at the moment. Why did you close this ticket? Have you find a bug and/or a workaround?

eblot avatar Sep 26 '20 09:09 eblot

A post about this issue seems to not have gotten into github, or an edit I made I didn't save before logging off for the day. This "issue" isn't an issue at all. The slave device in this case was performing an I2C Clock Stretch PRIOR to the ACK bit being shifted. In other words, the slave recognized it's ADDR[R] address and stopped the clock immediately in order to finish fetching the data. This is perfectly allowed by the letter of the law (although I wouldn't have implemented it that way, technically there's no issue) and so there was no problem with pyftdi. The "problem" was that "Clock stretching mode" wasn't enabled properly in my setup. My follow-up post of Aug 17 explains how to use the clock stretching for anyone else who runs into this.

I have made the following changes in i2c.py and this is my accompanying readme:

"This version has clockstretching enabled. blue wire of the dongle must be connected to SCL slave side of a diode (anode) and orange SCL_master must connect to SCL slave through the diode (cathode).

Note, these (my code cells) are tested with the following change in C:\ProgramData\Anaconda32bit\Lib\site-packages\pyftdi\i2c.py

#I2C Timings had copy-paste error in definition # Fix frequency for 3-phase clock if frequency <= 100E3: timings = self.I2C_100K elif frequency <= 400E3: timings = self.I2C_400K # was self.I2C_100K else: timings = self.I2C_1M # was self.I2C_100K

#cmd = bytearray(self._idle * self._ck_delay) is implemented to cleanup the stop.

def _do_prolog(self, i2caddress: int) -> None:
    if i2caddress is None:
        return
    self.log.debug('   prolog 0x%x', i2caddress >> 1)
    #cmd = bytearray(self._idle) #original
    cmd = bytearray(self._idle * self._ck_delay) #new
    cmd.extend(self._start)
    cmd.extend(self._write_byte)
    cmd.append(i2caddress)
    try:
        self._send_check_ack(cmd)
    except I2cNackError:
        self.log.warning('NACK @ 0x%02x', (i2caddress>>1))
        raise

"

EveOnlinePlayer avatar Sep 28 '20 13:09 EveOnlinePlayer

Another quick aside for those looking for absolute performance out of the FTDI driver. If any command is sent to the ftdi driver, such as an ADDR[R] or i2c.ftdi.read_data_bytes(1,4), or similar at least every 25ms or so, then the following round trip pc-slave-pc transactions to and from the slave(s) will be very fast indeed. This has nothing to do with pyftdi etc, but is a windows/USB latency problem. The FIRST transaction out of the USB port is slow, VERY slow. Using a trick or two (I recommend the ADDR command to avoid confusing someone who comes to the project later) to setup the USB transactions before a pile o data is blasted around might be helpful to some.

EveOnlinePlayer avatar Sep 28 '20 14:09 EveOnlinePlayer